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.
 
 
 
 
 
 

1915 lines
49 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
dslayer.c
Abstract:
Implemntation of 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 <md5.h>
#define LSADSP_MAX_ATTRS_ON_CREATE 3
NTSTATUS
LsapDsInitAllocAsNeededEx(
IN ULONG Options,
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
OUT PBOOLEAN Reset
)
{
NTSTATUS Status = STATUS_SUCCESS;
LsapEnterFunc( "LsapDsInitAllocAsNeededEx" );
*Reset = FALSE;
//
// Grab the lock
//
if ( !FLAG_ON( Options, LSAP_DB_NO_LOCK ) ) {
LsapDbAcquireLockEx( ObjectTypeId,
Options );
}
//
// If the LSA has no thread state yet, OR
// We aren't using Sam's transaction and
// the LSA hasn't yet opened one,
// do so now.
//
Status = ( *LsaDsStateInfo.DsFuncTable.pOpenTransaction ) ( Options );
if ( NT_SUCCESS( Status ) ) {
*Reset = TRUE;
} else {
if ( !FLAG_ON( Options, LSAP_DB_NO_LOCK ) ) {
LsapDbReleaseLockEx( ObjectTypeId,
Options );
}
}
LsapDsDebugOut(( DEB_FTRACE, "Leaving LsapDsInitAllocAsNeededEx ( %lu ): 0x%lx\n",
*Reset, Status ));
return( Status );
}
VOID
LsapDsDeleteAllocAsNeededEx(
IN ULONG Options,
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
IN BOOLEAN Reset
)
{
LsapDsDeleteAllocAsNeededEx2(
Options,
ObjectTypeId,
Reset,
FALSE // Rollback Transaction
);
}
VOID
LsapDsDeleteAllocAsNeededEx2(
IN ULONG Options,
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
IN BOOLEAN Reset,
IN BOOLEAN RollbackTransaction
)
{
LsapDsDebugOut(( DEB_FTRACE, "Entering LsapDsDeleteAllocAsNeededEx ( %lu )\n", Reset ));
if ( Reset ) {
if (RollbackTransaction)
{
( *LsaDsStateInfo.DsFuncTable.pAbortTransaction )( Options );
}
else
{
( *LsaDsStateInfo.DsFuncTable.pApplyTransaction )( Options );
}
}
//
// Release the lock if we had opened one
//
if ( !FLAG_ON( Options, LSAP_DB_NO_LOCK ) ) {
LsapDbReleaseLockEx( ObjectTypeId,
Options );
}
LsapDsDebugOut(( DEB_FTRACE, "LsapDsDeleteAllocAsNeededEx: 0\n" ));
}
NTSTATUS
LsapDsReadObjectSD(
IN LSAPR_HANDLE ObjectHandle,
OUT PSECURITY_DESCRIPTOR *ppSD
)
/*++
Routine Description:
This function will ready the security descriptor from the specified object
Arguments:
ObjectHandle - Object to read the SD from
ppSD -- Where the allocated security descriptor is returned. Allocated via
LsapAllocateLsaHeap.
Return Value:
Pointer to allocated memory on success or NULL on failure
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LSAP_DB_ATTRIBUTE Attribute;
BOOLEAN ReleaseState;
LSAP_DB_HANDLE InternalHandle = ( LSAP_DB_HANDLE )ObjectHandle;
LsapEnterFunc( "LsapDsReadObjectSD" );
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
InternalHandle->ObjectTypeId,
&ReleaseState );
if ( !NT_SUCCESS( Status ) ) {
LsapExitFunc( "LsapDsReadObjectSD", Status );
return( Status );
}
//
// Make sure we're coming in as DSA, so the access check that the DS does won't fail
//
LsapDsSetDsaFlags( TRUE );
LsapDbInitializeAttributeDs( &Attribute,
SecDesc,
NULL,
0,
FALSE );
Status = LsapDsReadAttributes(
(PUNICODE_STRING)&((LSAP_DB_HANDLE ) ObjectHandle)->PhysicalNameDs,
LSAPDS_OP_NO_LOCK,
&Attribute,
1 );
if ( Status == STATUS_SUCCESS ) {
*ppSD = LsapAllocateLsaHeap( Attribute.AttributeValueLength );
if ( *ppSD == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlCopyMemory( *ppSD, Attribute.AttributeValue, Attribute.AttributeValueLength );
}
MIDL_user_free( Attribute.AttributeValue );
} else {
*ppSD = NULL;
}
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
InternalHandle->ObjectTypeId,
ReleaseState );
LsapExitFunc( "LsapDsReadObjectSD", Status );
return( Status );
}
NTSTATUS
LsapDsTruncateNameToFitCN(
IN PUNICODE_STRING OriginalName,
OUT PUNICODE_STRING TruncatedName
)
/*++
Routine Description
This routine truncates the name to fix the 64 Char CN limit. The truncation
algorithm uses an MD5 Hash to compute the last 16 chars, the first 47
chars are left as they are. The 48'th char is a -. If the name is smaller
than the same limit the original name is returned as is copied into a
new buffer.
Parameters
OriginalName -- The original Name
TruncatedName -- The name truncated if required
Return Values
STATUS_SUCCESS
Other error codes that return a resource failure
--*/
{
MD5_CTX Md5Context;
ULONG i;
#define MAX_CN_SIZE 64
#define TO_HEX(x) (((x)<0xA)?(L'0'+(x)):(L'A'+(x)-0xA))
//
// Allocate memory to hold the new name
//
TruncatedName->Buffer = LsapAllocateLsaHeap(OriginalName->Length);
if (NULL==TruncatedName->Buffer)
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
if (OriginalName->Length<=MAX_CN_SIZE*sizeof(WCHAR))
{
//
// Original Name fits in CN, just copy and return it
//
RtlCopyMemory(
TruncatedName->Buffer,
OriginalName->Buffer,
OriginalName->Length
);
TruncatedName->Length = TruncatedName->MaximumLength = OriginalName->Length;
return (STATUS_SUCCESS);
}
//
// Name does not fit in, invent a unique suffix. This is done by
// computing a MD5 checksum of the original name and
// replacing the last 16 chars by hexprinted version of the lower
// nibbles of the hash
//
MD5Init(&Md5Context);
MD5Update(
&Md5Context,
(PUCHAR) OriginalName->Buffer,
OriginalName->Length
);
MD5Final(&Md5Context);
//
// The new name is the first 46 chars of the original name followed
// by a - and the checksum hex printed out behind. Only the low nibble
// of each byte is used so that only 16 chars of space is taken up
//
RtlCopyMemory(
TruncatedName->Buffer,
OriginalName->Buffer,
OriginalName->Length
);
TruncatedName->Buffer[MAX_CN_SIZE-MD5DIGESTLEN-2] = L'-';
for (i=0;i<MD5DIGESTLEN;i++)
{
TruncatedName->Buffer[MAX_CN_SIZE-MD5DIGESTLEN+i-1] = TO_HEX((0xf & Md5Context.digest[i]));
}
TruncatedName->Length = TruncatedName->MaximumLength = MAX_CN_SIZE * sizeof(WCHAR);
return STATUS_SUCCESS;
}
NTSTATUS
LsapDsGetPhysicalObjectName(
IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
IN BOOLEAN ObjectShouldExist,
IN PUNICODE_STRING LogicalNameU,
OUT OPTIONAL PUNICODE_STRING PhysicalNameU
)
/*++
Routine Description:
This function returns the Physical Name of an object
given an object information buffer. Memory will be allocated for
the Unicode String Buffers that will receive the name(s).
The Physical Name of an object is the full path of the object relative
to the root ot the Database. It is computed by concatenating the Physical
Name of the Container Object (if any), the Classifying Directory
corresponding to the object type id, and the Logical Name of the
object.
<Physical Name of Object> =
[<Physical Name of Container Object> "\"]
[<Classifying Directory> "\"] <Logical Name of Object>
If there is no Container Object (as in the case of the Policy object)
the <Physical Name of Container Object> and following \ are omitted.
If there is no Classifying Directory (as in the case of the Policy object)
the <Classifying Directory> and following \ are omitted. If neither
Container Object not Classifying Directory exist, the Logical and Physical
names coincide.
Note that memory is allocated by this routine for the output
Unicode string buffer(s). When the output Unicode String(s) are no
longer needed, the memory must be freed by call(s) to
RtlFreeUnicodeString().
Arguments:
ObjectInformation - Pointer to object information containing as a minimum
the object's Logical Name, Container Object's handle and object type
id.
DefaultName - If TRUE, use the default name for the object
LogicalNameU - Optional pointer to Unicode String structure which will
receive the Logical Name of the object. A buffer will be allocated
by this routine for the name text. This memory must be freed when no
longer needed by calling RtlFreeUnicodeString() wiht a pointer such
as LogicalNameU to the Unicode String structure.
PhysicalNameU - Optional pointer to Unicode String structure which will
receive the Physical Name of the object. A buffer will be allocated by
this routine for the name text. This memory must be freed when no
longer needed by calling RtlFreeUnicodeString() with a pointer such as
PhysicalNameU to the Unicode String structure.
Return Value:
NTSTATUS - Standard Nt Result Code
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources to
allocate the name string buffer for the Physical Name or
Logical Name.
STATUS_OBJECT_NAME_INVALID - Failed to produce the proper name
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = ObjectInformation->ObjectTypeId;
POBJECT_ATTRIBUTES ObjectAttributes = &ObjectInformation->ObjectAttributes;
PDSNAME Root = NULL, NewDsName = NULL;
PWSTR Name, LogicalName;
PBYTE Buffer = NULL;
UNICODE_STRING ObjectName, *Object=NULL;
ULONG Length = 0, InitialLength;
USHORT Len = 0, NameLen;
BOOLEAN NameSet = FALSE;
UNICODE_STRING TruncatedName;
LsapEnterFunc( "LsapDsGetPhysicalObjectName" );
RtlZeroMemory( &ObjectName, sizeof( UNICODE_STRING ) );
RtlZeroMemory( &TruncatedName, sizeof( UNICODE_STRING ) );
//
// The stages go as follows:
// Root DS domain path, obtained from LsaDsStateInfo
// Any container path specific to the object type for trusted domain/secret objects
// - or -
// the domain policy path or local policy path if it's a local or domain policy object
//
switch ( ObjectTypeId ) {
case TrustedDomainObject:
Root = LsaDsStateInfo.DsSystemContainer;
Object = LogicalNameU;
if ( ObjectShouldExist ) {
//
// Get the name of the object by searching for it
//
Status = LsapDsTrustedDomainObjectNameForDomain( Object,
FALSE,
&NewDsName );
if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
Status = LsapDsTrustedDomainObjectNameForDomain( Object,
TRUE,
&NewDsName );
}
if ( NT_SUCCESS( Status ) ) {
NameSet = TRUE;
}
}
break;
case NewTrustedDomainObject:
Root = LsaDsStateInfo.DsSystemContainer;
Object = LogicalNameU;
break;
case SecretObject:
Root = LsaDsStateInfo.DsSystemContainer;
Buffer = LsapAllocateLsaHeap( LogicalNameU->Length + sizeof( LSAP_DS_SECRET_POSTFIX ) -
sizeof(LSA_GLOBAL_SECRET_PREFIX) + sizeof( WCHAR ) );
if ( Buffer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
Name = (PWSTR)LogicalNameU->Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH;
NameLen = LogicalNameU->Length - (LSA_GLOBAL_SECRET_PREFIX_LENGTH * sizeof(WCHAR));
RtlCopyMemory( Buffer,
Name,
NameLen );
if ( !ObjectInformation->ObjectAttributeNameOnly ) {
RtlCopyMemory( Buffer + NameLen,
LSAP_DS_SECRET_POSTFIX,
sizeof( LSAP_DS_SECRET_POSTFIX ) );
}
RtlInitUnicodeString( &ObjectName, (PWSTR)Buffer );
Object = &ObjectName;
}
break;
default:
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// Build the physical name
//
if ( NT_SUCCESS ( Status ) ) {
if ( !NameSet ) {
//
// Truncate the name if necessary to fit the common name
// attribute in the schema
//
Status = LsapDsTruncateNameToFitCN(
Object,
&TruncatedName
);
if (!NT_SUCCESS(Status))
{
goto Error;
}
//
// Allocate a default buffer to use...
//
InitialLength = LsapDsLengthAppendRdnLength( Root,
Object->Length + 4 * sizeof( WCHAR ) );
NewDsName = LsapAllocateLsaHeap( InitialLength );
if ( NewDsName == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
Length = AppendRDN( Root,
NewDsName,
InitialLength,
TruncatedName.Buffer,
LsapDsGetUnicodeStringLenNoNull( &TruncatedName ) / sizeof( WCHAR ),
ATT_COMMON_NAME );
if ( Length > InitialLength ) {
LsapFreeLsaHeap( NewDsName );
NewDsName = LsapAllocateLsaHeap( Length );
if ( NewDsName == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
#if DBG
InitialLength = Length;
#endif
Length = AppendRDN( Root,
NewDsName,
InitialLength,
TruncatedName.Buffer,
LsapDsGetUnicodeStringLenNoNull( &TruncatedName ) /
sizeof( WCHAR ),
ATT_COMMON_NAME );
if ( Length != 0 ) {
Status = STATUS_OBJECT_NAME_INVALID;
#if DBG
LsapDsDebugOut(( DEB_ERROR,
"Failed to build physical name for %wZ. We "
"allocated %lu but needed %lu\n",
Object,
InitialLength,
Length ));
#endif
}
}
}
}
//
// If we are creating a trusted domain name, make sure that the name isn't alread
// in use
//
if ( NT_SUCCESS( Status ) && ( ObjectTypeId == NewTrustedDomainObject ||
( ObjectTypeId == TrustedDomainObject && ObjectShouldExist == FALSE ) ) ) {
Status = LsapDsVerifyObjectExistenceByDsName( NewDsName );
if ( Status == STATUS_SUCCESS ) {
Status = STATUS_OBJECT_NAME_COLLISION;
} else if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
Status = STATUS_SUCCESS;
}
}
}
//
// Now, we copy off the newly allocated dsname string, and return that
//
if ( NT_SUCCESS( Status ) ) {
Length = ( LsapDsNameLenFromDsName( NewDsName ) *
sizeof( WCHAR ) ) + sizeof( WCHAR );
PhysicalNameU->Buffer = LsapAllocateLsaHeap( Length );
if ( PhysicalNameU->Buffer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlCopyMemory( PhysicalNameU->Buffer,
LsapDsNameFromDsName( NewDsName ),
Length );
RtlInitUnicodeString( PhysicalNameU,
PhysicalNameU->Buffer );
}
}
}
Error:
if ( ObjectTypeId == SecretObject ) {
LsapFreeLsaHeap( Buffer );
}
if ( NewDsName != NULL ) {
LsapFreeLsaHeap( NewDsName );
}
if ( TruncatedName.Buffer != NULL ) {
LsapFreeLsaHeap( TruncatedName.Buffer );
}
LsapExitFunc( "LsapDsGetPhysicalObjectName", Status );
return Status;
}
NTSTATUS
LsapDsOpenObject(
IN LSAP_DB_HANDLE ObjectHandle,
IN ULONG OpenMode,
OUT PVOID *pvKey
)
/*++
Routine Description:
Opens the object in the DS
Arguments:
ObjectHandle - Internal LSA object handle
OpenMode - How to open the object
pvKey - Where the key is returned
Return Value:
NTSTATUS - Standard Nt Result Code
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ATTR NameAttr;
ATTRVAL NameVal;
ATTRBLOCK NameBlock, ReturnBlock;
PDSNAME SearchName = NULL;
BOOLEAN ReleaseState = FALSE;
LSAP_DB_HANDLE InternalHandle = ( LSAP_DB_HANDLE )ObjectHandle;
BOOLEAN InitAllocSucceded = FALSE;
ULONG ObjClass;
LsapEnterFunc( "LsapDsOpenObject" );
//
// Ensure the handle is for one of the objects supported in the DS.
//
switch ( InternalHandle->ObjectTypeId ) {
case TrustedDomainObject:
ObjClass = CLASS_TRUSTED_DOMAIN;
break;
case SecretObject:
ObjClass = CLASS_SECRET;
break;
default:
ASSERT( FALSE );
return STATUS_INVALID_PARAMETER;
}
//
// Start a transaction.
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
InternalHandle->ObjectTypeId,
&ReleaseState );
if ( NT_SUCCESS( Status ) ) {
InitAllocSucceded = TRUE;
Status = LsapAllocAndInitializeDsNameFromUnicode(
(PLSA_UNICODE_STRING)&ObjectHandle->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( &ObjectHandle->PhysicalNameDs,
LSAPDS_READ_NO_LOCK,
&NameBlock,
&ReturnBlock);
if ( NT_SUCCESS( Status ) ) {
ULONG ReadVal;
ReadVal = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( ReturnBlock.pAttr );
if ( ReadVal != ObjClass ) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
}
}
if (InitAllocSucceded)
{
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
InternalHandle->ObjectTypeId,
ReleaseState );
}
LsapExitFunc( "LsapDsOpenObject", Status );
return( Status );
}
NTSTATUS
LsapDsVerifyObjectExistenceByDsName(
IN PDSNAME DsName
)
/*++
Routine Description:
Verifies if an object exists in the DS by opening it
Arguments:
DsName - pointer to an object's DS name
Return Value:
NTSTATUS - Standard Nt Result Code
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ATTR NameAttr;
ATTRVAL NameVal;
ATTRBLOCK NameBlock, ReturnBlock;
BOOLEAN ReleaseState = FALSE;
LsapEnterFunc( "LsapDsOpenObjectByDsName" );
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_NO_LOCK,
NullObject,
&ReleaseState );
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 = 0;
NameVal.pVal = NULL;
NameBlock.attrCount = 1;
NameBlock.pAttr = &NameAttr;
Status = LsapDsReadByDsName( DsName,
LSAPDS_READ_NO_LOCK,
&NameBlock,
&ReturnBlock);
}
LsapDsDeleteAllocAsNeededEx( LSAP_DB_NO_LOCK,
NullObject,
ReleaseState );
LsapExitFunc( "LsapDsOpenObjectByDsName", Status );
return( Status );
}
NTSTATUS
LsapDsOpenTransaction(
IN ULONG Options
)
/*++
Routine Description:
This function starts a transaction within the Ds.
Arguments:
Options - Options to use when opening the transaction. Valid values are:
Return Value:
NTSTATUS - Standard Nt Result Code
Result codes are those returned from the Registry Transaction
Package.
--*/
{
NTSTATUS Status;
PLSADS_PER_THREAD_INFO CurrentThreadInfo;
LsapEnterFunc( "LsapDsOpenTransaction" );
//
// If this operation doesn't do a DS transaction,
// we're done.
//
if ( Options & LSAP_DB_NO_DS_OP_TRANSACTION ) {
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Get an LSA thread state.
//
CurrentThreadInfo = LsapCreateThreadInfo();
if ( CurrentThreadInfo == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// If we don't already have a valid thread state, create one
//
if ( CurrentThreadInfo->DsThreadStateUseCount == 0 ) {
CurrentThreadInfo->InitialThreadState = THSave();
Status = LsapDsMapDsReturnToStatus( THCreate( CALLERTYPE_LSA ) );
if ( !NT_SUCCESS( Status ) ) {
THRestore( CurrentThreadInfo->InitialThreadState );
CurrentThreadInfo->InitialThreadState = NULL;
LsapClearThreadInfo();
goto Cleanup;
}
}
CurrentThreadInfo->DsThreadStateUseCount ++;
//
// If we ever want to not really start a transaction here,
// we have to ensure the same flag is passed to apply/abort and look
// at the flag there.
// if ( !FLAG_ON( Options, LSAP_DB_DS_OP_TRANSACTION ) ) {
if ( CurrentThreadInfo->DsTransUseCount == 0 ) {
if ( SampExistsDsTransaction() ) {
ASSERT( !SampExistsDsTransaction() );
DirTransactControl( TRANSACT_DONT_BEGIN_DONT_END );
CurrentThreadInfo->DsOperationCount++;
} else {
DirTransactControl( TRANSACT_BEGIN_DONT_END );
}
LsapDsDebugOut(( DEB_TRACE,
"DirTransactControl( TRANSACT_BEGIN_DONT_END ) in "
"LsapDsOpenTransaction\n" ));
}
CurrentThreadInfo->DsTransUseCount++;
// }
LsapDsSetDsaFlags( TRUE );
Status = STATUS_SUCCESS;
Cleanup:
LsapExitFunc( "LsapDsOpenTransaction", Status );
return( Status );
}
NTSTATUS
LsapDsOpenTransactionDummy(
IN ULONG Options
)
{
if ( Options & LSAP_DB_NO_DS_OP_TRANSACTION ) {
return STATUS_SUCCESS;
} else if ( !( Options & LSAP_DB_DS_OP_TRANSACTION )) {
return STATUS_SUCCESS;
} else {
// ASSERT( FALSE ); // just so i can see who the culprit is, ignorable
return STATUS_DIRECTORY_SERVICE_REQUIRED;
}
}
NTSTATUS
LsapDsApplyTransaction(
IN ULONG Options
)
/*++
Routine Description:
This function applies a transaction within the LSA Database.
Arguments:
Options - Specifies optional actions to be taken. The following
options are recognized, other options relevant to calling routines
are ignored.
LSAP_DB_NO_DS_OP_TRANSACTION - Nothing to do, get out
Return Value:
NTSTATUS - Standard Nt Result Code
Result codes are those returned from the Registry Transaction
Package.
--*/
{
NTSTATUS Status = STATUS_SUCCESS, Status2;
PLSADS_PER_THREAD_INFO CurrentThreadInfo;
LsapEnterFunc( "LsapDsApplyTransaction" );
//
// If this operation doesn't do a DS transaction,
// we're done.
//
if ( Options & LSAP_DB_NO_DS_OP_TRANSACTION ) {
LsapExitFunc( "LsapDsApplyTransaction", 0 );
return( STATUS_SUCCESS );
}
CurrentThreadInfo = LsapQueryThreadInfo();
//
// No thread info, no transaction
//
if ( CurrentThreadInfo == NULL ) {
LsapExitFunc( "LsapDsApplyTransaction", 0 );
return( STATUS_SUCCESS );
}
//
// If we're doing a transaction,
// decrement our count of embedded transactions.
//
if ( CurrentThreadInfo->DsTransUseCount > 0 ) {
CurrentThreadInfo->DsTransUseCount--;
//
// If this is our last transaction,
// commit it.
//
if ( CurrentThreadInfo->DsTransUseCount == 0 ) {
if ( CurrentThreadInfo->DsOperationCount == 0 ) {
//
// The only way we should get here is if we inadvertently marked a current
// "transaction" as active when it has never been used. As such, we can
// simply reset the flag.
//
if ( !SampExistsDsTransaction() ) {
DirTransactControl( TRANSACT_BEGIN_END );
} else {
ASSERT( SampExistsDsTransaction() );
CurrentThreadInfo->DsOperationCount = 1;
}
}
//
// If operations have been made to the DS,
// commit them now.
//
if ( CurrentThreadInfo->DsOperationCount > 0 ) {
Status = LsapDsCauseTransactionToCommitOrAbort( TRUE );
CurrentThreadInfo->DsOperationCount = 0;
}
}
}
//
// If we have a DS thread state,
// decrement our count of uses of that thread state.
//
if ( CurrentThreadInfo->DsThreadStateUseCount > 0 ) {
CurrentThreadInfo->DsThreadStateUseCount --;
//
// If we're now done with our DS thread state,
// destroy it.
//
if ( CurrentThreadInfo->DsThreadStateUseCount == 0 ) {
Status2 = LsapDsMapDsReturnToStatus( THDestroy( ) );
THRestore( CurrentThreadInfo->InitialThreadState );
CurrentThreadInfo->InitialThreadState = NULL;
ASSERT( NT_SUCCESS( Status2 ) );
if ( NT_SUCCESS( Status ) ) {
Status = Status2;
}
}
}
LsapClearThreadInfo();
LsapExitFunc( "LsapDsApplyTransaction", Status );
return( Status );
}
NTSTATUS
LsapDsApplyTransactionDummy(
IN ULONG Options
)
{
return( STATUS_SUCCESS );
}
NTSTATUS
LsapDsAbortTransaction(
IN ULONG Options
)
/*++
Routine Description:
This function aborts a transaction within the LSA Database.
WARNING: The Lsa Database must be in the locked state when this function
is called.
Arguments:
Options - Options to use for aborting the transaction
Return Value:
NTSTATUS - Standard Nt Result Code
Result codes are those returned from the Registry Transaction
Package.
--*/
{
NTSTATUS Status = STATUS_SUCCESS, Status2;
PLSADS_PER_THREAD_INFO CurrentThreadInfo;
LsapEnterFunc( "LsapDsAbortTransaction" );
//
// If this operation doesn't do a DS transaction,
// we're done.
//
if ( Options & LSAP_DB_NO_DS_OP_TRANSACTION ) {
LsapExitFunc( "LsapDsAbortTransaction", 0 );
return( STATUS_SUCCESS );
}
//
// No thread info, no transaction
//
CurrentThreadInfo = LsapQueryThreadInfo();
if ( CurrentThreadInfo == NULL ) {
LsapExitFunc( "LsapDsAbortTransaction", 0 );
return( STATUS_SUCCESS );
}
//
// If we're doing a transaction,
// decrement our count of embedded transactions.
//
if ( CurrentThreadInfo->DsTransUseCount > 0 ) {
CurrentThreadInfo->DsTransUseCount--;
//
// If this is our last transaction,
// abort it.
//
if ( CurrentThreadInfo->DsTransUseCount == 0 ) {
if ( CurrentThreadInfo->DsOperationCount > 0 ) {
//
// Since LsapDsCauseTransactionToCommitOrAbort will return an error
// if it successfully aborts a transaction, we throw the error code away
// We don't need to do anything with the transactions other than to ensure that
// they fail. We'll ensure this by issuing a bad dir call.
//
LsapDsCauseTransactionToCommitOrAbort( FALSE );
} else {
//
// We opened the transaction, but we never used it... Make sure to indicate
// that we don't have one
//
ASSERT(!SampExistsDsTransaction());
DirTransactControl( TRANSACT_BEGIN_END );
}
CurrentThreadInfo->DsOperationCount = 0;
}
}
//
// If we have a DS thread state,
// decrement our count of uses of that thread state.
//
if ( CurrentThreadInfo->DsThreadStateUseCount > 0 ) {
CurrentThreadInfo->DsThreadStateUseCount --;
//
// If we're now done with our DS thread state,
// destroy it.
//
if ( CurrentThreadInfo->DsThreadStateUseCount == 0 ) {
Status2 = LsapDsMapDsReturnToStatus( THDestroy( ) );
THRestore( CurrentThreadInfo->InitialThreadState );
CurrentThreadInfo->InitialThreadState = NULL;
ASSERT( NT_SUCCESS( Status2 ) );
if ( NT_SUCCESS( Status ) ) {
Status = Status2;
}
}
}
LsapClearThreadInfo();
LsapExitFunc( "LsapDsAbortTransaction", Status );
return( Status );
}
NTSTATUS
LsapDsAbortTransactionDummy(
IN ULONG Options
)
{
return( STATUS_SUCCESS );
}
NTSTATUS
LsapDsCreateObject(
IN PUNICODE_STRING ObjectPath,
IN ULONG Flags,
IN LSAP_DB_OBJECT_TYPE_ID ObjectType
)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG Value = 0;
ULONG Items = 1;
PBYTE NulLVal = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
ULONG cbSD = 0;
ATTRTYP AttrType[LSADSP_MAX_ATTRS_ON_CREATE] = {
ATT_OBJECT_CLASS,
ATT_NT_SECURITY_DESCRIPTOR,
0
};
ULONG ObjClass;
ATTRVAL Values[LSADSP_MAX_ATTRS_ON_CREATE] = {
{sizeof(ULONG), (PUCHAR)&ObjClass},
{0, (PUCHAR)NULL},
{sizeof(ULONG), (PUCHAR)&Value}
};
switch ( ObjectType ) {
case TrustedDomainObject:
ObjClass = CLASS_TRUSTED_DOMAIN;
break;
case SecretObject:
ObjClass = CLASS_SECRET;
break;
default:
Status = STATUS_INVALID_PARAMETER;
break;
}
if ( !NT_SUCCESS( Status ) ) {
goto Exit;
}
if (Flags & LSAPDS_CREATE_WITH_SD) {
//
// Get the default security descriptor with the owner
// set to the owner of the token of the caller
//
Status = LsapDsGetDefaultSecurityDescriptor(ObjClass,
&pSD,
&cbSD);
if (NT_SUCCESS(Status)) {
Values[Items].valLen = cbSD;
Values[Items].pVal = pSD;
Items++;
}
}
if ( !NT_SUCCESS( Status ) ) {
goto Exit;
}
if ( NT_SUCCESS( Status ) ) {
Status = LsapDsCreateAndSetObject(
ObjectPath,
Flags,
Items,
AttrType,
Values );
}
Exit:
if (pSD) {
LsapFreeLsaHeap(pSD);
}
LsapExitFunc( "LsapDsCreateObject", Status );
return( Status );
}
NTSTATUS
LsapDsDeleteObject(
IN PUNICODE_STRING ObjectPath
)
{
NTSTATUS Status = STATUS_SUCCESS;
PDSNAME DsName;
BOOLEAN ReleaseState;
LsapEnterFunc( "LsapDsDeleteObject" );
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_NO_LOCK,
NullObject,
&ReleaseState );
if ( NT_SUCCESS( Status ) ) {
Status = LsapAllocAndInitializeDsNameFromUnicode(
ObjectPath,
&DsName
);
if ( NT_SUCCESS( Status ) ) {
Status = LsapDsRemove( DsName );
THFree( DsName );
}
LsapDsDeleteAllocAsNeededEx( LSAP_DB_NO_LOCK,
NullObject,
ReleaseState );
}
LsapExitFunc( "LsapDsDeleteObject", Status );
return( Status );
}
NTSTATUS
LsapDsWriteAttributesByDsName(
IN PDSNAME ObjectPath,
IN PLSAP_DB_ATTRIBUTE Attributes,
IN ULONG AttributeCount,
IN ULONG Options
)
{
NTSTATUS Status = STATUS_SUCCESS;
ATTRBLOCK AttrBlock;
PATTR Attrs;
ULONG i, AttrBlockIndex = 0;
LsapEnterFunc( "LsapDsWriteAttributesByDsName" );
LsapDsSetDsaFlags( TRUE );
//
// Ok, first, build the list of Ds attributes
//
Attrs = LsapDsAlloc( sizeof( ATTR ) * AttributeCount );
if ( Attrs == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
for ( i = 0 ; i < AttributeCount && NT_SUCCESS( Status ); i++ ) {
if ( !Attributes[ i ].PseudoAttribute ) {
Status = LsapDsLsaAttributeToDsAttribute( &Attributes[ i ],
&Attrs[ AttrBlockIndex++ ] );
}
}
if ( NT_SUCCESS( Status ) ) {
AttrBlock.attrCount = AttrBlockIndex;
AttrBlock.pAttr = Attrs;
//
// Now, simply write it out
//
Status = LsapDsWriteByDsName( ObjectPath,
LSAPDS_REPLACE_ATTRIBUTE | Options,
&AttrBlock );
}
}
LsapExitFunc( "LsapDsWriteAttributesByDsName", Status );
return( Status );
}
NTSTATUS
LsapDsWriteAttributes(
IN PUNICODE_STRING ObjectPath,
IN PLSAP_DB_ATTRIBUTE Attributes,
IN ULONG AttributeCount,
IN ULONG Options
)
{
NTSTATUS Status = STATUS_SUCCESS;
PDSNAME DsName = NULL;
BOOLEAN ReleaseState;
LsapEnterFunc( "LsapDsWriteAttributes" );
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_NO_LOCK,
NullObject,
&ReleaseState );
if ( !NT_SUCCESS( Status ) ) {
LsapExitFunc( "LsapDsWriteAttributes", Status );
return( Status );
}
//
// Build the DSName
//
Status = LsapAllocAndInitializeDsNameFromUnicode(
ObjectPath,
&DsName );
if ( NT_SUCCESS( Status ) ) {
Status = LsapDsWriteAttributesByDsName( DsName,
Attributes,
AttributeCount,
Options );
LsapDsFree( DsName );
}
LsapDsDeleteAllocAsNeededEx( LSAP_DB_NO_LOCK,
NullObject,
ReleaseState );
LsapExitFunc( "LsapDsWriteAttributes", Status );
return( Status );
}
NTSTATUS
LsapDsReadAttributesByDsName(
IN PDSNAME ObjectPath,
IN ULONG Options,
IN OUT PLSAP_DB_ATTRIBUTE Attributes,
IN ULONG AttributeCount
)
{
NTSTATUS Status = STATUS_SUCCESS;
ATTRBLOCK AttrBlock;
ATTRBLOCK ReadAttr;
PATTR Attrs;
ULONG i, j;
BOOLEAN ReleaseState;
LsapEnterFunc( "LsapDsReadAttributesByDsName" );
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_NO_LOCK,
NullObject,
&ReleaseState );
if ( !NT_SUCCESS( Status ) ) {
LsapExitFunc( "LsapDsReadAttributesByDsName", Status );
return( Status );
}
LsapDsSetDsaFlags( TRUE );
//
// Ok, first, build the list of Ds attributes
//
Attrs = LsapDsAlloc( sizeof( ATTR ) * AttributeCount );
if ( Attrs == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
for ( i = 0 ; i < AttributeCount; i++ ) {
Attrs[i].attrTyp = Attributes[i].DsAttId;
Attrs[i].AttrVal.valCount = 0;
Attrs[i].AttrVal.pAVal = NULL;
}
AttrBlock.attrCount = AttributeCount;
AttrBlock.pAttr = Attrs;
//
// Now, simply write it out
//
Status = LsapDsReadByDsName( ObjectPath, Options, &AttrBlock, &ReadAttr );
//
// If that worked, fill in the rest of our attributes
//
if ( NT_SUCCESS( Status ) ) {
#if DBG
if ( AttributeCount != ReadAttr.attrCount ) {
LsapDsDebugOut(( DEB_WARN,
"LsapDsReadAttributes: Expected %lu attributes, got %lu\n",
AttributeCount, ReadAttr.attrCount ));
}
#endif
for ( j = 0; j < AttributeCount; j++ ) {
for ( i = 0 ; i < ReadAttr.attrCount && NT_SUCCESS( Status ); i++ ) {
if ( Attributes[ j ].DsAttId == ReadAttr.pAttr[ i ].attrTyp ) {
Status = LsapDsDsAttributeToLsaAttribute( ReadAttr.pAttr[i].AttrVal.pAVal,
&Attributes[j] );
break;
}
}
//
// If we got throught the loop and the value wasn't found, see if our attribute
// can default to zero. If not, it's an error
//
if ( i >= ReadAttr.attrCount ) {
if ( Attributes[ j ].CanDefaultToZero == TRUE ) {
Attributes[ j ].AttributeValue = NULL;
Attributes[ j ].AttributeValueLength = 0;
} else {
Status = STATUS_NOT_FOUND;
LsapDsDebugOut(( DEB_ERROR,
"Attribute %wZ not found on object %wZ\n",
&Attributes[ j ].AttributeName,
ObjectPath ));
}
}
}
} else if ( AttributeCount == 1 && Status == STATUS_NOT_FOUND ) {
//
// If we were only looking for one attribute, it's possible that its ok for that
// attribute to be null.
//
if ( Attributes[ 0 ].CanDefaultToZero ) {
Status = STATUS_SUCCESS;
Attributes[ 0 ].AttributeValue = NULL;
Attributes[ 0 ].AttributeValueLength = 0;
}
}
}
LsapDsDeleteAllocAsNeededEx( LSAP_DB_NO_LOCK,
NullObject,
ReleaseState );
LsapExitFunc( "LsapDsReadAttributesByDsName", Status );
return( Status );
}
NTSTATUS
LsapDsReadAttributes(
IN PUNICODE_STRING ObjectPath,
IN ULONG Options,
IN OUT PLSAP_DB_ATTRIBUTE Attributes,
IN ULONG AttributeCount
)
{
NTSTATUS Status = STATUS_SUCCESS;
PDSNAME DsName = NULL;
BOOLEAN ReleaseState;
LsapEnterFunc( "LsapDsReadAttributes" );
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_NO_LOCK,
NullObject,
&ReleaseState );
if ( !NT_SUCCESS( Status ) ) {
LsapExitFunc( "LsapDsReadAttributes", Status );
return( Status );
}
//
// Build the DSName
//
Status = LsapAllocAndInitializeDsNameFromUnicode(
ObjectPath,
&DsName );
if ( NT_SUCCESS( Status ) ) {
Status = LsapDsReadAttributesByDsName( DsName,
Options,
Attributes,
AttributeCount );
LsapDsFree( DsName );
}
LsapDsDeleteAllocAsNeededEx( LSAP_DB_NO_LOCK,
NullObject,
ReleaseState );
LsapExitFunc( "LsapDsReadAttributes", Status );
return( Status );
}
NTSTATUS
LsapDsDeleteAttributes(
IN PUNICODE_STRING ObjectPath,
IN OUT PLSAP_DB_ATTRIBUTE Attributes,
IN ULONG AttributeCount
)
{
NTSTATUS Status = STATUS_SUCCESS;
ATTRBLOCK AttrBlock;
PATTR Attrs;
ULONG i;
LsapEnterFunc( "LsapDsDeleteAttributes" );
LsapDsSetDsaFlags( TRUE );
//
// Ok, first, build the list of Ds attributes
//
Attrs = LsapDsAlloc( sizeof( ATTR ) * AttributeCount );
if ( Attrs == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
for ( i = 0 ; i < AttributeCount && NT_SUCCESS( Status ); i++ ) {
Attributes[i].AttributeValueLength = 0;
Attributes[i].AttributeValue = NULL;
Status = LsapDsLsaAttributeToDsAttribute( &Attributes[i], &Attrs[i] );
}
if ( NT_SUCCESS( Status ) ) {
AttrBlock.attrCount = AttributeCount;
AttrBlock.pAttr = Attrs;
//
// Now, simply write it out
//
Status = LsapDsWrite( ObjectPath, AT_CHOICE_REMOVE_ATT, &AttrBlock );
}
}
LsapExitFunc( "LsapDsDeleteAttributes", Status );
return( Status );
}
NTSTATUS
LsapDsTrustedDomainSidToLogicalName(
IN PSID Sid,
OUT PUNICODE_STRING LogicalNameU
)
/*++
Routine Description:
This function generates the Logical Name (Internal LSA Database Name)
of a trusted domain object from its Sid. Currently, only the Relative
Arguments:
Sid - Pointer to the Sid to be looked up. It
LogicalNameU - Pointer to a Unicode String structure that will receive
the Logical Name. Note that memory for the string buffer in this
Unicode String will be allocated by this routine if successful. The
caller must free this memory after use by calling RtlFreeUnicodeString.
Return Value:
NTSTATUS - Standard Nt Status code
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
to allocate buffer for Unicode String name.
--*/
{
NTSTATUS Status;
ATTR SidAttr;
ATTRVAL SidVal;
ATTRBLOCK SidBlock;
PDSNAME FoundName = NULL;
BOOLEAN ReleaseState;
WCHAR RdnBuffer[MAX_RDN_SIZE + 1];
ULONG RdnLen;
ATTRBLOCK ReadBlock, ReturnedBlock;
ATTRTYP RdnType;
ATTR ReadAttr[] = {
{LsapDsAttributeIds[ LsapDsAttrTrustPartner ], {0, NULL} }
};
LsapEnterFunc( "LsapDsTrustedDomainSidToLogicalName" );
//
// First, verify that the given Sid is valid
//
if (!RtlValidSid( Sid )) {
LsapExitFunc( "LsapDsTrustedDomainSidToLogicalName", STATUS_INVALID_PARAMETER );
return( STATUS_INVALID_PARAMETER );
}
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_DS_OP_TRANSACTION |
LSAP_DB_READ_ONLY_TRANSACTION,
TrustedDomainObject,
&ReleaseState );
if ( NT_SUCCESS( Status ) ) {
//
// Check for the existence of the object
//
SidAttr.attrTyp = ATT_SECURITY_IDENTIFIER;
SidAttr.AttrVal.valCount = 1;
SidAttr.AttrVal.pAVal = &SidVal;
SidVal.valLen = RtlLengthSid( Sid );
SidVal.pVal = (PBYTE)Sid;
SidBlock.attrCount = 1;
SidBlock.pAttr = &SidAttr;
Status = LsapDsSearchUnique( LSAPDS_SEARCH_LEVEL | LSAPDS_OP_NO_TRANS,
LsaDsStateInfo.DsSystemContainer,
&SidAttr,
1,
&FoundName );
if ( NT_SUCCESS( Status ) ) {
ReadBlock.attrCount = sizeof( ReadAttr ) / sizeof( ATTR );
ReadBlock.pAttr = ReadAttr;
Status = LsapDsReadByDsName( FoundName,
LSAPDS_READ_NO_LOCK,
&ReadBlock,
&ReturnedBlock );
if ( NT_SUCCESS( Status ) && LogicalNameU ) {
LSAPDS_ALLOC_AND_COPY_STRING_TO_UNICODE_ON_SUCCESS(
Status,
LogicalNameU,
ReturnedBlock.pAttr[0].AttrVal.pAVal[ 0 ].pVal,
ReturnedBlock.pAttr[0].AttrVal.pAVal[ 0 ].valLen );
}
LsapFreeLsaHeap( FoundName );
}
LsapDsDeleteAllocAsNeededEx( LSAP_DB_DS_OP_TRANSACTION |
LSAP_DB_READ_ONLY_TRANSACTION,
TrustedDomainObject,
ReleaseState );
}
LsapExitFunc( "LsapDsTrustedDomainSidToLogicalName", Status );
return( Status );
}
VOID
LsapDsContinueTransaction(
VOID
)
/*++
Routine Description:
Call this function when we've just done a Dir* call and want to continue
the transaction.
Arguments:
None.
Return Value:
None.
--*/
{
PLSADS_PER_THREAD_INFO CurrentThreadInfo;
LsapEnterFunc( "LsapDsContinueTransaction" );
CurrentThreadInfo = LsapQueryThreadInfo();
ASSERT( CurrentThreadInfo != NULL && CurrentThreadInfo->DsThreadStateUseCount > 0 );
if ( CurrentThreadInfo != NULL ) {
//
// Tell the DS that there's more to come.
//
//
// Current code assumes every Dir*
// uses a transaction and if one is not started starts one. However, DirNotifyUnRegister
// doesn't start a transaction at all. And sometimes Dir* calls may not start
// a transaction due to memory shortage or the service is shutting down. Therefore
// it is best to check if there is actually a transaction or not.
//
if ( CurrentThreadInfo->DsTransUseCount && SampExistsDsTransaction() ) {
DirTransactControl( TRANSACT_DONT_BEGIN_DONT_END );
CurrentThreadInfo->DsOperationCount++;
}
}
LsapExitFunc( "LsapDsContinueTransaction", 0 );
}