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.
5227 lines
157 KiB
5227 lines
157 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dbobject.c
|
|
|
|
Abstract:
|
|
|
|
Local Security Authority - LSA Database Public Object Management Routines
|
|
|
|
This module contains the public routines that perform LSA Database object
|
|
manipulation. These routines are exported to the rest of the
|
|
LSA, function prototypes of these routines will be found in db.h. These
|
|
exported routines present an implementation-independent hierarchic
|
|
object-based view of the LSA Database and are used exclusively by the
|
|
LSA API. See the Additional Notes further below for a description of
|
|
the LSA Database model.
|
|
|
|
Routines in this module that are private to the object management
|
|
function have function prototypes in dbp.h.
|
|
|
|
Author:
|
|
|
|
Scott Birrell (ScottBi) August 26, 1991
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
Revision History:
|
|
|
|
Notes on the LSA Database Architecture
|
|
|
|
OBJECT STRUCTURE
|
|
|
|
The LSA Database is an hierarchic structure containing "objects" of
|
|
several "types". Objects have either a "name" or a Sid depending only
|
|
on object type, and may have data stored with them under named
|
|
"attributes". The database hierarchy contains a single root object
|
|
called the Lsa Database object and having name "Policy". This object
|
|
represents the entire LSA Database. Currently, the Lsa Database has a
|
|
simple hierarchy consisting of only two levels.
|
|
|
|
Policy
|
|
|
|
Account Objects, Trusted Domain Objects, Secret Objects
|
|
|
|
The Policy object is called a "Container Object" for the other
|
|
object types. The attributes of the Policy object house information
|
|
that applies generally to the whole database. The single Policy object
|
|
has name "Policy".
|
|
|
|
Account objects represent those user accounts which are treated specially
|
|
on the local system, but not necessarily so on other systems. Such
|
|
accounts may have additional privileges, or system quotas for example.
|
|
Account objects are referenced by Sid.
|
|
|
|
TrustedDomain objects describe domains which the system has a trust
|
|
relationship with. These objects are referenced by Sid.
|
|
|
|
Secret Objects are named entities containing information that is protected
|
|
in some way. Secret objects are referenced by name.
|
|
|
|
OBJECT ACCESS AND DATABASE SECURITY
|
|
|
|
Each object in the LSA Database is protected by a Security Descriptor which
|
|
contains a Discretionary Access Control List (DACL) defining which groups
|
|
can access the object and in which ways. Before an object can be
|
|
accessed, it must first be "opened" with the desired accesses requested
|
|
that are needed to perform the desired operations on the object. Opening
|
|
an object returns a "handle" to the object. This handle may then be
|
|
specified on Lsa services that access the object. After use, the handle
|
|
to the object should then be "closed". Closing the handle renders it
|
|
invalid.
|
|
|
|
CONCURRENCY OF ACCESS
|
|
|
|
More than one handle may be open to an object concurrently, possibly with
|
|
different accesses granted.
|
|
|
|
PERMANENCY OF OBJECTS
|
|
|
|
All LSA Database objects are backed by non-volatile storage media, that is,
|
|
they remain in existence until deleted via the LsaDelete() service.
|
|
The Policy object cannot be deleted and the single object of this type cannot
|
|
be created via the public LSA service interface.
|
|
|
|
Objects will not be deleted while there are open handles to them.
|
|
When access to an object is no longer required, the handle should be
|
|
"closed".
|
|
|
|
DATABASE DESIGN
|
|
|
|
The LSA Database is of an hierarchic design permitting future extension.
|
|
Currently the database has the following simple hierarchy:
|
|
|
|
Policy Object (name = Policy)
|
|
|
|
Account Objects TrustedDomain Objects Secret Objects
|
|
|
|
The single object of type Policy is at the topmost level and serves as
|
|
a parent or "container" object for objects of the other three types.
|
|
Since named objects of different types may potentially reside in the
|
|
same container object in the future, an object is referenced uniquely
|
|
only if the object name and type together with the identity of its
|
|
container object (currently always the Policy object) are known.
|
|
To implement this kind of reference easily, objects of the same type
|
|
are held within a "classifying directory" which has a name derived
|
|
from the object's type as follows:
|
|
|
|
Object Type Containing Directory Name
|
|
|
|
Policy Not required
|
|
Account Accounts
|
|
TrustedDomain Domains
|
|
Secret Secrets
|
|
|
|
IMPLEMENTATION NOTES
|
|
|
|
The LSA Database is currently implemented as a subtree of the Configuration
|
|
Registry. This subtree has the following form
|
|
|
|
\Policy\Accounts\<account_object_Rid>\<account_object_attribute_name>
|
|
\Domains\<trusted_domain_Rid>\<trus_domain_object_attribute_name>
|
|
\Secrets\<secret_name>\<secret_object_attribute_name>
|
|
\<policy_object_attribute_name>
|
|
|
|
where each item between \..\ is the name of a Registry Key and
|
|
"Rid" is a character name made out of the Relative Id (lowest
|
|
subauthority extracted from the object's Sid). Named object attributes
|
|
can have binary data "values".
|
|
|
|
--*/
|
|
#include <lsapch2.h>
|
|
#include "dbp.h"
|
|
// #include "adtp.h"
|
|
#include <accctrl.h>
|
|
#include <sertlp.h>
|
|
|
|
NTSTATUS
|
|
LsapDbOpenObject(
|
|
IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG Options,
|
|
OUT PLSAPR_HANDLE ObjectHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function opens an existing object in the LSA Database. An error
|
|
is returned if the object does not already exist. The LSA Database must
|
|
be already locked when calling this function and any container handle
|
|
must have been validated as having the necessary access for creation
|
|
of an object of the given type.
|
|
|
|
Arguments:
|
|
|
|
ObjectInformation - Pointer to information describing this object. The
|
|
following information items must be specified:
|
|
|
|
o Object Type Id
|
|
o Object Logical Name (as ObjectAttributes->ObjectName, a pointer to
|
|
a Unicode string)
|
|
o Container object handle (for any object except the root Policy object).
|
|
o Object Sid (if any)
|
|
|
|
All other fields in ObjectAttributes portion of ObjectInformation
|
|
such as SecurityDescriptor are ignored.
|
|
|
|
DesiredAccess - Specifies the Desired accesses to the Lsa object
|
|
|
|
Options - Specifies optional additional actions to be taken:
|
|
|
|
LSAP_DB_TRUSTED - A trusted handle is wanted regardless of the trust
|
|
status of any container handle provided in ObjectInformation.
|
|
|
|
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit replicator notification
|
|
on object updates. This flag will be stored in the handle
|
|
created for the object and retrieved when committing an update
|
|
to the object via LsapDbDereferenceObject().
|
|
|
|
ObjectHandle - Receives the handle to the object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard NT status code
|
|
|
|
STATUS_INVALID_PARAMETER - One or more parameters invalid.
|
|
- Invalid syntax of parameters, e.g Sid
|
|
- Sid not specified when required for object type
|
|
- Name specified when not allowed.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
to complete the request (e.g. memory for reading object's
|
|
Security Descriptor).
|
|
|
|
STATUS_OBJECT_NOT_FOUND - Object does not exist.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG SecurityDescriptorLength;
|
|
LSAP_DB_HANDLE NewObjectHandle = NULL;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
|
|
OBJECT_ATTRIBUTES OpenKeyObjectAttributes;
|
|
ULONG States = Options & LSAP_DB_STATE_MASK;
|
|
ULONG ResetStates = 0;
|
|
LSAPR_HANDLE OutputHandle = NULL;
|
|
LSAP_DB_HANDLE InternalOutputHandle = NULL;
|
|
|
|
PSECURITY_DESCRIPTOR SavedSecurityDescriptor =
|
|
ObjectInformation->ObjectAttributes.SecurityDescriptor;
|
|
|
|
//
|
|
// Validate the Object Information parameter.
|
|
//
|
|
|
|
Status = LsapDbVerifyInformationObject( ObjectInformation );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenObjectError;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate and initialize a handle for the object. The object's
|
|
// Registry Key, Logical and Physical Names will be derived from
|
|
// the given ObjectInformation and pointers to them will be stored in
|
|
// the handle.
|
|
//
|
|
Status = LsapDbCreateHandle( ObjectInformation,
|
|
Options,
|
|
LSAP_DB_CREATE_OPEN_EXISTING,
|
|
&OutputHandle );
|
|
InternalOutputHandle = ( LSAP_DB_HANDLE ) OutputHandle;
|
|
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
goto OpenObjectError;
|
|
}
|
|
|
|
//
|
|
// Now attempt to open the object's Registry Key. Store the Registry
|
|
// Key handle in the object's handle.
|
|
//
|
|
// Avoid this if we're using a cached handle and the key is already open.
|
|
//
|
|
if ( InternalOutputHandle->KeyHandle == NULL ) {
|
|
|
|
if ( !LsapDsIsHandleDsHandle( InternalOutputHandle ) ) {
|
|
|
|
InternalOutputHandle->fWriteDs = FALSE;
|
|
|
|
Status = LsapRegOpenObject( InternalOutputHandle,
|
|
KEY_READ | KEY_WRITE,
|
|
&(InternalOutputHandle->KeyHandle)
|
|
);
|
|
|
|
} else {
|
|
|
|
InternalOutputHandle->fWriteDs = TRUE;
|
|
|
|
Status = LsapDsOpenObject( InternalOutputHandle,
|
|
KEY_READ | KEY_WRITE,
|
|
&(InternalOutputHandle->KeyHandle)
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status ) && InternalOutputHandle->ObjectTypeId == PolicyObject) {
|
|
|
|
Status = LsapRegOpenObject( InternalOutputHandle,
|
|
KEY_READ | KEY_WRITE,
|
|
&( InternalOutputHandle->KeyHandle ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
InternalOutputHandle->KeyHandle = NULL; // For cleanup purposes
|
|
goto OpenObjectError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The object exists. Unless access checking is to be bypassed, we
|
|
// need to access the object's Security Descriptor and perform an
|
|
// access check. The Security Descriptor is stored as the object's
|
|
// SecDesc attribute, so we need to read this. First, we must query the
|
|
// size of the Security Descriptor to determine how much memory to
|
|
// allocate for reading it. The query is done by issuing a read of the
|
|
// object's SecDesc subkey with a NULL output buffer and zero size
|
|
// specified.
|
|
//
|
|
|
|
if (!(InternalOutputHandle->Trusted)) {
|
|
|
|
if ( LsapDsIsWriteDs( InternalOutputHandle ) ) {
|
|
|
|
Status = LsapDsReadObjectSD(
|
|
OutputHandle,
|
|
&SecurityDescriptor );
|
|
|
|
} else {
|
|
|
|
Status = LsapRegReadObjectSD(
|
|
OutputHandle,
|
|
&SecurityDescriptor );
|
|
|
|
}
|
|
|
|
//
|
|
// Request the desired accesses and store them in the object's handle.
|
|
// granted.
|
|
//
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
ObjectInformation->ObjectAttributes.SecurityDescriptor = SecurityDescriptor;
|
|
|
|
Status = LsapDbRequestAccessObject(
|
|
OutputHandle,
|
|
ObjectInformation,
|
|
DesiredAccess,
|
|
Options
|
|
);
|
|
}
|
|
|
|
//
|
|
// If the accesses are granted, the open has completed successfully.
|
|
// Store the container object handle in the object's handle and
|
|
// return the handle to the caller..
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Bug #340164: don't disclose information about secret's existence
|
|
// by returning "access denied"; map to "not found"
|
|
//
|
|
|
|
if ( Status == STATUS_ACCESS_DENIED &&
|
|
LsapGlobalRestrictAnonymous &&
|
|
( ObjectInformation->ObjectTypeId == SecretObject ||
|
|
ObjectInformation->ObjectTypeId == AccountObject )&&
|
|
ObjectInformation->ObjectAttributes.RootDirectory != NULL &&
|
|
((( LSAP_DB_HANDLE )ObjectInformation->ObjectAttributes.RootDirectory)->Options & LSAP_DB_OPENED_BY_ANONYMOUS )) {
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
goto OpenObjectError;
|
|
}
|
|
|
|
//
|
|
// See if there is an existing identical handle in the cache
|
|
//
|
|
|
|
if ( !LsapDbFindIdenticalHandleInTable( &OutputHandle ) ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto OpenObjectError;
|
|
}
|
|
InternalOutputHandle = ( LSAP_DB_HANDLE ) OutputHandle;
|
|
|
|
}
|
|
|
|
*ObjectHandle = OutputHandle;
|
|
|
|
OpenObjectFinish:
|
|
|
|
//
|
|
// Restore the saved Security Descriptor reference in the object
|
|
// information.
|
|
//
|
|
|
|
ObjectInformation->ObjectAttributes.SecurityDescriptor =
|
|
SavedSecurityDescriptor;
|
|
|
|
//
|
|
// If necessary, free the memory allocated for the Security Descriptor
|
|
//
|
|
|
|
if (SecurityDescriptor != NULL) {
|
|
|
|
LsapFreeLsaHeap( SecurityDescriptor );
|
|
}
|
|
|
|
return(Status);
|
|
|
|
OpenObjectError:
|
|
|
|
//
|
|
// If necessary, free the handle we created.
|
|
//
|
|
|
|
if (OutputHandle != NULL) {
|
|
|
|
LsapDbDereferenceHandle(OutputHandle, FALSE);
|
|
}
|
|
|
|
goto OpenObjectFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbCreateObject(
|
|
IN OUT PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG CreateDisposition,
|
|
IN ULONG Options,
|
|
IN OPTIONAL PLSAP_DB_ATTRIBUTE Attributes,
|
|
IN OUT ULONG *TypeSpecificAttributeCount,
|
|
IN ULONG TypeSpecificAttributeAllocated,
|
|
OUT PLSAPR_HANDLE ObjectHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates an object in the LSA Database, together with
|
|
the set of attributes, such as Security Descriptor that are common
|
|
to all object types. The object will be left in the open state
|
|
and the caller may use the returned handle to create the type-
|
|
specific attributes.
|
|
|
|
NOTE: For an object creation, it is the responsibility of the calling
|
|
LSA object creation routine to verify that the necessary access to the
|
|
container object is granted. That access is dependent on the type of
|
|
LSA object being created.
|
|
|
|
WARNING: The Lsa Database must be in the locked state when this function
|
|
is called. No Lsa Database transaction may be pending when
|
|
this function is called.
|
|
|
|
Arguments:
|
|
|
|
ObjectInformation - Pointer to information describing this object. The
|
|
following information items must be specified:
|
|
|
|
o Object Type Id
|
|
o Object Logical Name (as ObjectAttributes->ObjectName, a pointer to
|
|
a Unicode string)
|
|
o Container object handle (for any object except the root Policy object).
|
|
o Object Sid (if any)
|
|
|
|
All other fields in ObjectAttributes portion of ObjectInformation
|
|
such as SecurityDescriptor are ignored.
|
|
|
|
DesiredAccess - Specifies the Desired accesses to the object.
|
|
|
|
CreateDisposition - Specifies the Creation Disposition. This is the
|
|
action to take depending on whether the object already exists.
|
|
|
|
LSA_OBJECT_CREATE - Create the object if it does not exist. If
|
|
the object already exists, return an error.
|
|
|
|
LSA_OBJECT_OPEN_IF - Create the object if it does not exist. If
|
|
the object already exists, just open it.
|
|
|
|
Options - Specifies optional information and actions to be taken
|
|
|
|
LSAP_DB_TRUSTED - A Trusted Handle is wanted regardless of the
|
|
Trust status of any container handle provided in
|
|
ObjectInformation.
|
|
|
|
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit notification of the
|
|
object creation to Replicator.
|
|
|
|
Note, this routine performs a complete database transaction so
|
|
there is no option to start one.
|
|
|
|
Attributes - Optional pointer to an array of attribute
|
|
names and values. These are specific to the type of object.
|
|
|
|
TypeSpecificAttributeCount - Number of elements in the array
|
|
referenced by the Attributes parameter. This can be updated
|
|
if attributes are added.
|
|
|
|
TypeSpecificAttributeAllocated -- the number attributes that have been
|
|
preallocated in Attributes
|
|
|
|
ObjectHandle - Receives the handle to the object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_INVALID_PARAMETER - The given Sid is invalid.
|
|
|
|
STATUS_OBJECT_NAME_EXISTS - An object having the given Sid
|
|
already exists and has been opened because LSA_OBJECT_OPEN_IF
|
|
disposition has been specified. This is a warning only.
|
|
|
|
STATUS_OBJECT_NAME_COLLISION - An object having the given Sid
|
|
already exists but has not been opened because LSA_OBJECT_CREATE
|
|
disposition has been specified. This is an error.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, SecondaryStatus, IgnoreStatus;
|
|
OBJECT_ATTRIBUTES OpenKeyObjectAttributes;
|
|
ULONG CloseOptions, Index, TrustAttribs = 0, TrustType, TransOptions = 0, EndTransOptions = 0;
|
|
BOOLEAN CreatedObject = FALSE;
|
|
BOOLEAN OpenedObject = FALSE;
|
|
BOOLEAN OpenedTransaction = FALSE;
|
|
LSAPR_HANDLE OutputHandle = NULL;
|
|
LSAP_DB_HANDLE InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
|
|
LSAP_DB_HANDLE ContainerHandle = NULL;
|
|
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = ObjectInformation->ObjectTypeId;
|
|
PDSNAME ObjectXRef = NULL;
|
|
|
|
LsapDsDebugOut(( DEB_FTRACE, "LsapDbCreateObject\n" ));
|
|
|
|
//
|
|
// Sanity check the values
|
|
//
|
|
ASSERT(*TypeSpecificAttributeCount <= TypeSpecificAttributeAllocated);
|
|
|
|
//
|
|
// Get the container handle
|
|
//
|
|
|
|
ContainerHandle = (LSAP_DB_HANDLE) ObjectInformation->ObjectAttributes.RootDirectory;
|
|
|
|
//
|
|
// Verify the creation disposition.
|
|
//
|
|
|
|
if (((CreateDisposition & ~LSAP_DB_CREATE_VALID_EXTENDED_FLAGS) != LSAP_DB_OBJECT_CREATE) &&
|
|
((CreateDisposition & ~LSAP_DB_CREATE_VALID_EXTENDED_FLAGS) != LSAP_DB_OBJECT_OPEN_IF)) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
|
|
//
|
|
// Try to open the object. It is permissible for the object to
|
|
// exist already if LSA_OBJECT_OPEN_IF disposition was specified.
|
|
//
|
|
|
|
if ( FLAG_ON( CreateDisposition, LSAP_DB_CREATE_OBJECT_IN_DS ) ) {
|
|
|
|
Options |= LSAP_DB_OBJECT_SCOPE_DS;
|
|
}
|
|
|
|
Status = LsapDbOpenObject(
|
|
ObjectInformation,
|
|
DesiredAccess,
|
|
Options,
|
|
&OutputHandle
|
|
);
|
|
|
|
InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// The object was successfully opened. If LSA_OBJECT_OPEN_IF
|
|
// disposition was specified, we're done, otherwise, we
|
|
// return a collision error.
|
|
//
|
|
|
|
OpenedObject = TRUE;
|
|
|
|
if ( (CreateDisposition & ~LSAP_DB_CREATE_VALID_EXTENDED_FLAGS) == LSAP_DB_OBJECT_OPEN_IF) {
|
|
|
|
Status = STATUS_OBJECT_NAME_EXISTS;
|
|
|
|
goto CreateObjectFinish;
|
|
}
|
|
|
|
if ((CreateDisposition & ~LSAP_DB_CREATE_VALID_EXTENDED_FLAGS) == LSAP_DB_OBJECT_CREATE ) {
|
|
|
|
Status = STATUS_OBJECT_NAME_COLLISION;
|
|
|
|
LsapLogError(
|
|
"LsapDbCreateObject: 0x%lx\n", Status
|
|
);
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The object was not successfully opened. If this is for any
|
|
// reason other than that the object was not found, return an error.
|
|
//
|
|
|
|
if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
//
|
|
// If this is a trusted domain object, and the Ds is installed, let's try and open it
|
|
// by Sid as well. If this succeeds, then we'll consider that the object exists, even
|
|
// if the names don't match..
|
|
//
|
|
|
|
if ( LsaDsStateInfo.UseDs && ObjectInformation->ObjectTypeId == TrustedDomainObject &&
|
|
ObjectInformation->Sid != NULL ) {
|
|
|
|
Status = LsapDsTrustedDomainSidToLogicalName( ObjectInformation->Sid,
|
|
NULL );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// The object was successfully opened. If LSA_OBJECT_OPEN_IF
|
|
// disposition was specified, we're done, otherwise, we
|
|
// return a collision error.
|
|
//
|
|
if ( (CreateDisposition & ~LSAP_DB_CREATE_VALID_EXTENDED_FLAGS) == LSAP_DB_OBJECT_OPEN_IF) {
|
|
|
|
Status = STATUS_OBJECT_NAME_EXISTS;
|
|
|
|
goto CreateObjectFinish;
|
|
}
|
|
|
|
if ((CreateDisposition & ~LSAP_DB_CREATE_VALID_EXTENDED_FLAGS) == LSAP_DB_OBJECT_CREATE ) {
|
|
|
|
Status = STATUS_OBJECT_NAME_COLLISION;
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
}
|
|
|
|
if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The object was not found. Prepare to create it. First, we need to
|
|
// check that any maximum limit on the number of objects of this type
|
|
// imposed will not be exceeded.
|
|
//
|
|
|
|
Status = LsapDbCheckCountObject(ObjectTypeId);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
//
|
|
// Next we need to create a handle for the new object.
|
|
//
|
|
if ( ObjectInformation->ObjectTypeId == TrustedDomainObject ) {
|
|
|
|
ObjectInformation->ObjectTypeId = NewTrustedDomainObject;
|
|
}
|
|
|
|
Status = LsapDbCreateHandle( ObjectInformation,
|
|
Options,
|
|
LSAP_DB_CREATE_HANDLE_MORPH,
|
|
&OutputHandle );
|
|
InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
|
|
if ( ObjectInformation->ObjectTypeId == NewTrustedDomainObject ) {
|
|
|
|
ObjectInformation->ObjectTypeId = TrustedDomainObject;
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
//
|
|
// If this is a Ds object, indicate so in the handle, so that in the
|
|
// subsequent call to RequestAccessNewObject, we can properly make the
|
|
// determiniation on whether to abort if this is a backup domain controller
|
|
//
|
|
if ( LsapDsIsHandleDsHandle( InternalOutputHandle ) ) {
|
|
|
|
LsapDsSetHandleWriteDs( InternalOutputHandle );
|
|
}
|
|
|
|
|
|
//
|
|
// Verify that the requested accesses can be given to the handle that
|
|
// has been opened and grant them if so.
|
|
//
|
|
Status = LsapDbRequestAccessNewObject(
|
|
OutputHandle,
|
|
ObjectInformation,
|
|
DesiredAccess,
|
|
Options
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
//
|
|
// Open a Registry transaction for creation of the object.
|
|
//
|
|
if ( ObjectTypeId == TrustedDomainObject ) {
|
|
|
|
TransOptions |= LSAP_DB_READ_ONLY_TRANSACTION; // Skip the registry transaction
|
|
EndTransOptions |= LSAP_DB_READ_ONLY_TRANSACTION;
|
|
}
|
|
|
|
if ( ObjectTypeId == SecretObject && !FLAG_ON( Options, LSAP_DB_OBJECT_SCOPE_DS ) ) {
|
|
|
|
TransOptions |= LSAP_DB_NO_DS_OP_TRANSACTION;
|
|
EndTransOptions |= LSAP_DB_NO_DS_OP_TRANSACTION;
|
|
}
|
|
|
|
if ( ObjectTypeId == PolicyObject ||
|
|
ObjectTypeId == AccountObject ) {
|
|
|
|
TransOptions |= LSAP_DB_NO_DS_OP_TRANSACTION;
|
|
EndTransOptions |= LSAP_DB_NO_DS_OP_TRANSACTION;
|
|
}
|
|
|
|
Status = LsapDbOpenTransaction( TransOptions );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
OpenedTransaction = TRUE;
|
|
|
|
//
|
|
// Add a registry transaction to create the Registry key for the new
|
|
// Database object.
|
|
//
|
|
|
|
//
|
|
// If we have a Ds name, do a Ds create
|
|
//
|
|
if ( !LsapDsIsHandleDsHandle( InternalOutputHandle ) ) {
|
|
|
|
//
|
|
// Create it in the registry
|
|
//
|
|
InternalOutputHandle->fWriteDs = FALSE;
|
|
Status = LsapRegCreateObject( &InternalOutputHandle->PhysicalNameU,
|
|
ObjectTypeId );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Don't actually attempt to create an account object
|
|
//
|
|
if ( InternalOutputHandle->ObjectTypeId != AccountObject) {
|
|
|
|
Status = LsapDsCreateObject(
|
|
&InternalOutputHandle->PhysicalNameDs,
|
|
FLAG_ON( ContainerHandle->Options, LSAP_DB_TRUSTED) ?
|
|
LSAPDS_CREATE_TRUSTED :
|
|
0,
|
|
ObjectTypeId );
|
|
|
|
if ((STATUS_ACCESS_DENIED == Status)
|
|
&& (ObjectInformation->ObjectTypeId == TrustedDomainObject)) {
|
|
|
|
//
|
|
// If this is an attempt to create a trust, check to see
|
|
// if this request will be granted by a control access
|
|
// right.
|
|
//
|
|
Status = LsapCheckTDOCreationByControlAccess(ObjectInformation,
|
|
Attributes,
|
|
*TypeSpecificAttributeCount);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Request is allowed, create as trusted, along with
|
|
// the default security descriptor
|
|
//
|
|
|
|
Status = LsapDsCreateObject(
|
|
&InternalOutputHandle->PhysicalNameDs,
|
|
LSAPDS_CREATE_TRUSTED | LSAPDS_CREATE_WITH_SD,
|
|
ObjectTypeId );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Adjust the handle access
|
|
//
|
|
InternalOutputHandle->GrantedAccess = (TRUSTED_READ |
|
|
TRUSTED_SET_AUTH |
|
|
DELETE);
|
|
|
|
//
|
|
// Modify the attributes on the trust object to
|
|
// reflect the fact that this object was created
|
|
// via the control access right.
|
|
//
|
|
Status = LsapUpdateTDOAttributesForCreation(&InternalOutputHandle->PhysicalNameDs,
|
|
Attributes,
|
|
TypeSpecificAttributeCount,
|
|
TypeSpecificAttributeAllocated);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Since we didn't actually create an object, don't do any notification
|
|
//
|
|
Options |= LSAP_DB_OMIT_REPLICATOR_NOTIFICATION;
|
|
goto CreateObjectReset;
|
|
}
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
//
|
|
// Create the Security Descriptor for the new object. This will be
|
|
// stored in Self-Relative form as the value of the SecDesc attribute
|
|
// of the new object.
|
|
//
|
|
|
|
Status = LsapDbCreateSDAttributeObject(
|
|
OutputHandle,
|
|
ObjectInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
//
|
|
// The self-relative SD returned is not needed here or by callers of
|
|
// this routine.
|
|
//
|
|
|
|
if (ObjectInformation->ObjectAttributes.SecurityDescriptor != NULL) {
|
|
|
|
RtlFreeHeap(
|
|
RtlProcessHeap(),
|
|
0,
|
|
ObjectInformation->ObjectAttributes.SecurityDescriptor
|
|
);
|
|
|
|
ObjectInformation->ObjectAttributes.SecurityDescriptor = NULL;
|
|
}
|
|
|
|
//
|
|
// Write the type-specific attributes (if any) for the object).
|
|
//
|
|
|
|
if (*TypeSpecificAttributeCount != 0) {
|
|
|
|
Status = LsapDbWriteAttributesObject(
|
|
OutputHandle,
|
|
Attributes,
|
|
*TypeSpecificAttributeCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
}
|
|
|
|
CreateObjectReset:
|
|
|
|
//
|
|
// Apply the Registry Transaction to create the object. Note
|
|
// that we have to create the object before we can open its
|
|
// registry key for placement within the handle.
|
|
//
|
|
|
|
Status = LsapDbResetStates(
|
|
OutputHandle,
|
|
Options | EndTransOptions | LSAP_DB_FINISH_TRANSACTION,
|
|
ObjectTypeId,
|
|
SecurityDbNew,
|
|
Status
|
|
);
|
|
|
|
OpenedTransaction = FALSE;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
//
|
|
// Increment the count of objects created. It should not have
|
|
// changed since we're still holding the LSA Database lock.
|
|
// NOTE: Count is decremented on error inside LsapDbDeleteObject()
|
|
//
|
|
|
|
LsapDbIncrementCountObject(ObjectInformation->ObjectTypeId);
|
|
|
|
CreatedObject = TRUE;
|
|
|
|
if ( !LsapDsIsWriteDs( OutputHandle ) ) {
|
|
|
|
//
|
|
// The object has now been created. We need to obtain its Registry
|
|
// Key handle so that we can save it in the Object Handle.
|
|
// Setup Object Attributes structure for opening the Registry key of
|
|
// the object. Specify as path the Physical Name of the object, this
|
|
// being the path of the object's Registry Key relative to the
|
|
// LSA Database root key.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&OpenKeyObjectAttributes,
|
|
&InternalOutputHandle->PhysicalNameU,
|
|
OBJ_CASE_INSENSITIVE,
|
|
LsapDbState.DbRootRegKeyHandle,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Now attempt to open the object's Registry Key. Store the Registry
|
|
// Key handle in the object's handle.
|
|
//
|
|
|
|
Status = RtlpNtOpenKey(
|
|
(PHANDLE) &InternalOutputHandle->KeyHandle,
|
|
KEY_READ | KEY_WRITE,
|
|
&OpenKeyObjectAttributes,
|
|
0L
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
InternalOutputHandle->KeyHandle = NULL;
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Add the new object to the in-memory cache (if any). This is done
|
|
// after all other actions, so that no removal from the cache is required
|
|
// on the error paths. If the object cannot be added to the cache, the
|
|
// cache routine automatically disables the cache.
|
|
//
|
|
|
|
if ( ObjectTypeId == AccountObject &&
|
|
LsapDbIsCacheSupported( AccountObject ) &&
|
|
LsapDbIsCacheValid( AccountObject )) {
|
|
|
|
IgnoreStatus = LsapDbCreateAccount(
|
|
InternalOutputHandle->Sid,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
CreateObjectFinish:
|
|
|
|
//
|
|
// Return NULL or a handle to the newly created and opened object.
|
|
//
|
|
|
|
*ObjectHandle = OutputHandle;
|
|
|
|
LsapDsDebugOut(( DEB_FTRACE, "LsapDbCreateObject: 0x%lx\n", Status ));
|
|
|
|
return(Status);
|
|
|
|
CreateObjectError:
|
|
|
|
//
|
|
// Cleanup after error. Various variables are set non-null if
|
|
// there is cleanup work to do.
|
|
//
|
|
|
|
//
|
|
// If necessary, abort the Registry Transaction to create the object
|
|
//
|
|
|
|
if (OpenedTransaction) {
|
|
|
|
SecondaryStatus = LsapDbResetStates(
|
|
OutputHandle,
|
|
EndTransOptions | LSAP_DB_FINISH_TRANSACTION,
|
|
ObjectTypeId,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
LsapDbSetStatusFromSecondary( Status, SecondaryStatus );
|
|
}
|
|
|
|
//
|
|
// If we opened the object, close it.
|
|
//
|
|
|
|
if (OpenedObject) {
|
|
|
|
CloseOptions = 0;
|
|
SecondaryStatus = LsapDbCloseObject( &OutputHandle, CloseOptions, Status );
|
|
|
|
if ( Status != SecondaryStatus && !NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
LsapLogError(
|
|
"LsapDbCreateObject: LsapDbCloseObject failed 0x%lx\n",
|
|
SecondaryStatus
|
|
);
|
|
}
|
|
|
|
OutputHandle = NULL;
|
|
InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
|
|
|
|
} else if (CreatedObject) {
|
|
|
|
//
|
|
// If we created the object, convert its handle into a trusted
|
|
// handle and delete it.
|
|
//
|
|
|
|
InternalOutputHandle->Trusted = TRUE;
|
|
|
|
SecondaryStatus = LsarDelete( OutputHandle );
|
|
|
|
if (!NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
LsapLogError(
|
|
"LsapDbCreateObject: LsarDeleteObject failed 0x%lx\n",
|
|
SecondaryStatus
|
|
);
|
|
}
|
|
|
|
} else if (OutputHandle != NULL) {
|
|
|
|
//
|
|
// If we just created the handle, free it.
|
|
//
|
|
|
|
LsapDbDereferenceHandle( OutputHandle, FALSE );
|
|
|
|
OutputHandle = NULL;
|
|
InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
|
|
}
|
|
|
|
goto CreateObjectFinish;
|
|
|
|
DBG_UNREFERENCED_PARAMETER( CloseOptions );
|
|
}
|
|
|
|
GUID LsapDsTrustedDomainNamePropSet = {0x4886566c,0xaf31,0x11d2,0xb7,0xdf,0x00,0x80,0x5f,0x48,0xca,0xeb};
|
|
GUID LsapDsTrustedDomainAuthPropSet = {0x736e4812,0xaf31,0x11d2,0xb7,0xdf,0x00,0x80,0x5f,0x48,0xca,0xeb};
|
|
GUID LsapDsTrustedDomainPosixPropSet = {0x9567ca92,0xaf31,0x11d2,0xb7,0xdf,0x00,0x80,0x5f,0x48,0xca,0xeb};
|
|
|
|
static LSAP_DS_OBJECT_ACCESS_MAP TrustedDomainAccessMap[] = {
|
|
|
|
{ TRUSTED_QUERY_DOMAIN_NAME, ACTRL_DS_READ_PROP, ACCESS_PROPERTY_SET_GUID, &LsapDsTrustedDomainNamePropSet },
|
|
{ TRUSTED_SET_AUTH, ACTRL_DS_WRITE_PROP, ACCESS_PROPERTY_SET_GUID, &LsapDsTrustedDomainAuthPropSet },
|
|
{ TRUSTED_QUERY_AUTH, ACTRL_DS_READ_PROP, ACCESS_PROPERTY_SET_GUID, &LsapDsTrustedDomainAuthPropSet },
|
|
{ TRUSTED_QUERY_POSIX, ACTRL_DS_READ_PROP, ACCESS_PROPERTY_SET_GUID, &LsapDsTrustedDomainPosixPropSet },
|
|
{ TRUSTED_SET_POSIX, ACTRL_DS_WRITE_PROP, ACCESS_PROPERTY_SET_GUID, &LsapDsTrustedDomainPosixPropSet }
|
|
};
|
|
|
|
#define TrustedDomainAccessMapSize ((sizeof(TrustedDomainAccessMap))/(sizeof(TrustedDomainAccessMap[0])))
|
|
|
|
static LSAP_DS_OBJECT_ACCESS_MAP TrustedDomainAsSecretAccessMap[] = {
|
|
{ SECRET_QUERY_VALUE, ACTRL_DS_READ_PROP, ACCESS_PROPERTY_SET_GUID, &LsapDsTrustedDomainAuthPropSet },
|
|
{ SECRET_SET_VALUE, ACTRL_DS_READ_PROP, ACCESS_PROPERTY_SET_GUID, &LsapDsTrustedDomainAuthPropSet }
|
|
};
|
|
|
|
#define TrustedDomainAsSecretAccessMapSize ((sizeof(TrustedDomainAsSecretAccessMap))/(sizeof(TrustedDomainAsSecretAccessMap[0])))
|
|
|
|
OBJECT_TYPE_LIST TrustedDomainTypeList[] = {
|
|
{ ACCESS_OBJECT_GUID, 0, &LsapDsGuidList[ LsapDsGuidTrust ] },
|
|
|
|
{ ACCESS_PROPERTY_SET_GUID,0,&LsapDsTrustedDomainNamePropSet},
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidFlatName ] },
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidSid ] },
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidAttributes ] },
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidDirection ] },
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidPartner] },
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidType ] },
|
|
|
|
{ ACCESS_PROPERTY_SET_GUID, 0, &LsapDsTrustedDomainAuthPropSet},
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidInitialIncoming ] },
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidInitialOutgoing ] },
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidIncoming ] },
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidOutgoing ] },
|
|
|
|
{ ACCESS_PROPERTY_SET_GUID, 0,&LsapDsTrustedDomainPosixPropSet},
|
|
{ ACCESS_PROPERTY_GUID, 0,&LsapDsGuidList[ LsapDsGuidPosix ] },
|
|
|
|
};
|
|
|
|
#define TDTYPE_LIST_SIZE ((sizeof( TrustedDomainTypeList ))/(sizeof( OBJECT_TYPE_LIST )))
|
|
|
|
GUID LsapDsSecretPropSet = {0x9fa81d6c,0xaf69,0x11d2,0xb7,0xdf,0x00,0x80,0x5f,0x48,0xca,0xeb};
|
|
|
|
static LSAP_DS_OBJECT_ACCESS_MAP GlobalSecretAccessMap[] = {
|
|
{ SECRET_QUERY_VALUE, ACTRL_DS_READ_PROP, ACCESS_PROPERTY_SET_GUID, &LsapDsSecretPropSet },
|
|
{ SECRET_SET_VALUE, ACTRL_DS_WRITE_PROP, ACCESS_PROPERTY_SET_GUID, &LsapDsSecretPropSet }
|
|
};
|
|
|
|
#define GlobalSecretAccessMapSize ( (sizeof(GlobalSecretAccessMap))/(sizeof(GlobalSecretAccessMap[0])))
|
|
|
|
|
|
OBJECT_TYPE_LIST GlobalSecretTypeList[] = {
|
|
|
|
{ ACCESS_OBJECT_GUID, 0, &LsapDsGuidList[ LsapDsGuidSecret ] },
|
|
{ ACCESS_PROPERTY_SET_GUID, 0, &LsapDsSecretPropSet },
|
|
{ ACCESS_PROPERTY_SET_GUID, 0, &LsapDsGuidList[LsapDsGuidCurrent] },
|
|
{ ACCESS_PROPERTY_SET_GUID, 0, &LsapDsGuidList[LsapDsGuidCurrentTime] },
|
|
{ ACCESS_PROPERTY_SET_GUID, 0, &LsapDsGuidList[LsapDsGuidPrevious] },
|
|
{ ACCESS_PROPERTY_SET_GUID, 0, &LsapDsGuidList[LsapDsguidPreviousTime] }
|
|
};
|
|
|
|
#define SECRETTYPE_LIST_SIZE ( sizeof( GlobalSecretTypeList ) / sizeof( OBJECT_TYPE_LIST ) )
|
|
|
|
// generic read
|
|
#define LSAP_DS_GENERIC_READ_MAPPING ((STANDARD_RIGHTS_READ) | \
|
|
(ACTRL_DS_LIST) | \
|
|
(ACTRL_DS_READ_PROP) | \
|
|
(ACTRL_DS_LIST_OBJECT))
|
|
|
|
// generic execute
|
|
#define LSAP_DS_GENERIC_EXECUTE_MAPPING ((STANDARD_RIGHTS_EXECUTE) | \
|
|
(ACTRL_DS_LIST_OBJECT))
|
|
// generic right
|
|
#define LSAP_DS_GENERIC_WRITE_MAPPING ((STANDARD_RIGHTS_WRITE) | \
|
|
(ACTRL_DS_SELF) | \
|
|
(ACTRL_DS_WRITE_PROP))
|
|
// generic all
|
|
|
|
#define LSAP_DS_GENERIC_ALL_MAPPING ((STANDARD_RIGHTS_REQUIRED) | \
|
|
(ACTRL_DS_CREATE_CHILD) | \
|
|
(ACTRL_DS_DELETE_CHILD) | \
|
|
(ACTRL_DS_DELETE_TREE) | \
|
|
(ACTRL_DS_READ_PROP) | \
|
|
(ACTRL_DS_WRITE_PROP) | \
|
|
(ACTRL_DS_LIST) | \
|
|
(ACTRL_DS_LIST_OBJECT) | \
|
|
(ACTRL_DS_CONTROL_ACCESS) | \
|
|
(ACTRL_DS_SELF))
|
|
|
|
//
|
|
// Standard DS generic access rights mapping
|
|
//
|
|
|
|
#define LSAP_DS_GENERIC_MAPPING { \
|
|
LSAP_DS_GENERIC_READ_MAPPING, \
|
|
LSAP_DS_GENERIC_WRITE_MAPPING, \
|
|
LSAP_DS_GENERIC_EXECUTE_MAPPING, \
|
|
LSAP_DS_GENERIC_ALL_MAPPING}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbRequestAccessObject(
|
|
IN OUT LSAPR_HANDLE ObjectHandle,
|
|
IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG Options
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function performs an access check for an LSA Database object. While
|
|
impersonating an RPC client, the specified Desired Accesses are reconciled
|
|
with the Discretionary Access Control List (DACL) in the object's
|
|
Security Descriptor. Note that the object's Security Descriptor is
|
|
passed explicitly so that this routine can be called for new objects
|
|
for which a SD has been constructed but not yet written to the
|
|
Registry.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Handle to object. The handle will receive the
|
|
granted accesses if the call is successful.
|
|
|
|
ObjectInformation - Pointer to object's information. As a minimum, the
|
|
object's Security Descriptor must be set up.
|
|
|
|
DesiredAccess - Specifies a mask of the access types desired to the
|
|
object.
|
|
|
|
Options - Specifies optional actions to be taken
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Not all of the Desired Accessed can be
|
|
granted to the caller.
|
|
|
|
STATUS_BACKUP_CONTROLLER - A create, update or delete operation
|
|
is not allowed for a non-trusted client for this object on a BDC,
|
|
because the object is global to all DC's for a domain and is replicated.
|
|
|
|
Errors from RPC client impersonation
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS, RevertStatus = STATUS_SUCCESS, AccessStatus = STATUS_SUCCESS;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
|
|
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = InternalHandle->ObjectTypeId;
|
|
LSAP_DB_OBJECT_TYPE_ID ObjectTypeIdForGenericMapping;
|
|
ULONG EffectiveOptions = Options | InternalHandle->Options;
|
|
GENERIC_MAPPING LsapDsGenericMap = LSAP_DS_GENERIC_MAPPING;
|
|
|
|
#ifdef LSAP_TRACK_HANDLE
|
|
HANDLE ClientToken = NULL;
|
|
#endif
|
|
|
|
ULONG TrustedDomainTypeCount = 0;
|
|
|
|
ULONG GlobalSecretTypeCount = sizeof( GlobalSecretTypeList ) / sizeof( OBJECT_TYPE_LIST );
|
|
ULONG GrantedAccess = 0;
|
|
POBJECT_TYPE_LIST TypeListToCheck = NULL;
|
|
LSAP_DS_OBJECT_ACCESS_MAP * MappingTableToUse = NULL;
|
|
ULONG TypeListToCheckCount = 0;
|
|
ULONG MappingTableSize=0;
|
|
ULONG AccessStatusArray[ TDTYPE_LIST_SIZE ];
|
|
ULONG GrantedAccessArray[ TDTYPE_LIST_SIZE ];
|
|
BOOLEAN fAtLeastOneAccessGranted = FALSE;
|
|
BOOLEAN fNoAccessRequested = FALSE;
|
|
BOOLEAN IsAnonymous = FALSE;
|
|
BOOLEAN MustLogAnonymousAccessDenied = FALSE;
|
|
|
|
//
|
|
// AccessStatusArray and GrantedAccessArray are set to the larger of the
|
|
// two counts: TDTYPE_LIST_SIZE and SECRETTYPE_LIST_SIZE
|
|
//
|
|
|
|
ASSERT( TDTYPE_LIST_SIZE > SECRETTYPE_LIST_SIZE );
|
|
|
|
// This routine should not be called on trusted clients
|
|
ASSERT( !InternalHandle->Trusted );
|
|
|
|
|
|
//
|
|
// Get the correct object type id for generic mapping
|
|
//
|
|
|
|
if ( FLAG_ON( InternalHandle->Options, LSAP_DB_DS_TRUSTED_DOMAIN_AS_SECRET ) )
|
|
{
|
|
ObjectTypeIdForGenericMapping = SecretObject;
|
|
}
|
|
else
|
|
{
|
|
ObjectTypeIdForGenericMapping = ObjectTypeId;
|
|
}
|
|
|
|
//
|
|
// Map any Generic Access Types to Specific Access Types
|
|
//
|
|
|
|
RtlMapGenericMask(
|
|
&DesiredAccess,
|
|
&(LsapDbState.DbObjectTypes[ObjectTypeIdForGenericMapping].GenericMapping)
|
|
);
|
|
|
|
//
|
|
// Common path for Object Open and Creation. We need to reconcile
|
|
// the desired accesses to the object with the Discretionary Access
|
|
// Control List contained in the Security Descriptor. Note that this
|
|
// needs to be done even for newly created objects, since they are
|
|
// being opened as well as created.
|
|
|
|
//
|
|
// Impersonate the client thread prior to doing an access check.
|
|
//
|
|
|
|
if ( Options & LSAP_DB_USE_LPC_IMPERSONATE ) {
|
|
Status = LsapImpersonateClient( );
|
|
} else {
|
|
Status = I_RpcMapWin32Status(RpcImpersonateClient(0));
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Reconcile the desired access with the discretionary ACL
|
|
// of the Resultant Descriptor. Note that this operation is performed
|
|
// even if we are just creating the object since the object is to
|
|
// be opened.
|
|
//
|
|
|
|
switch ( InternalHandle->ObjectTypeId ) {
|
|
case TrustedDomainObject:
|
|
|
|
if ( !LsaDsStateInfo.UseDs ) {
|
|
|
|
break;
|
|
}
|
|
|
|
if ( FLAG_ON( InternalHandle->Options, LSAP_DB_DS_TRUSTED_DOMAIN_AS_SECRET ) )
|
|
{
|
|
MappingTableToUse = TrustedDomainAsSecretAccessMap;
|
|
MappingTableSize = TrustedDomainAsSecretAccessMapSize;
|
|
}
|
|
else
|
|
{
|
|
MappingTableToUse = TrustedDomainAccessMap;
|
|
MappingTableSize = TrustedDomainAccessMapSize;
|
|
}
|
|
|
|
TypeListToCheck = TrustedDomainTypeList;
|
|
TypeListToCheckCount = TDTYPE_LIST_SIZE ;
|
|
|
|
break;
|
|
|
|
case SecretObject:
|
|
|
|
//
|
|
// Bug #340164: anonymous users are not allowed to look at secrets
|
|
//
|
|
|
|
if ( LsapGlobalRestrictAnonymous &&
|
|
(( LSAP_DB_HANDLE )ObjectInformation->ObjectAttributes.RootDirectory)->Options & LSAP_DB_OPENED_BY_ANONYMOUS ) {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we are running on a client, don't specify a property list, or it'll fail
|
|
//
|
|
|
|
if ( !LsaDsStateInfo.UseDs ) {
|
|
|
|
break;
|
|
}
|
|
|
|
if ( FLAG_ON( InternalHandle->Options, LSAP_DB_DS_TRUSTED_DOMAIN_AS_SECRET ) ) {
|
|
|
|
TypeListToCheck = TrustedDomainTypeList;
|
|
TypeListToCheckCount = TDTYPE_LIST_SIZE;
|
|
MappingTableToUse = TrustedDomainAsSecretAccessMap;
|
|
MappingTableSize = TrustedDomainAsSecretAccessMapSize;
|
|
|
|
} else if ( FLAG_ON( InternalHandle->Options, LSAP_DB_OBJECT_SCOPE_DS ) ) {
|
|
|
|
TypeListToCheck = GlobalSecretTypeList;
|
|
TypeListToCheckCount = SECRETTYPE_LIST_SIZE;
|
|
MappingTableToUse = GlobalSecretAccessMap;
|
|
MappingTableSize = GlobalSecretAccessMapSize;
|
|
}
|
|
|
|
break;
|
|
|
|
case AccountObject:
|
|
|
|
//
|
|
// Bug #340164: anonymous users are not allowed to look at accounts
|
|
//
|
|
|
|
if ( LsapGlobalRestrictAnonymous &&
|
|
(( LSAP_DB_HANDLE )ObjectInformation->ObjectAttributes.RootDirectory)->Options & LSAP_DB_OPENED_BY_ANONYMOUS ) {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
|
|
case PolicyObject:
|
|
{
|
|
HANDLE ClientToken;
|
|
|
|
//
|
|
// Determine if the caller is anonymous
|
|
//
|
|
|
|
Status = NtOpenThreadToken( NtCurrentThread(),
|
|
TOKEN_QUERY,
|
|
FALSE,
|
|
&ClientToken );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
UCHAR Buffer[ 128 ];
|
|
PTOKEN_USER User ;
|
|
ULONG Size ;
|
|
|
|
User = (PTOKEN_USER) Buffer ;
|
|
|
|
Status = NtQueryInformationToken(
|
|
ClientToken,
|
|
TokenUser,
|
|
User,
|
|
sizeof( Buffer ),
|
|
&Size );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
if ( LsapGlobalRestrictAnonymous &&
|
|
RtlEqualSid( User->User.Sid, LsapAnonymousSid ) ) {
|
|
|
|
if ( LsapProductType == NtProductLanManNt ) {
|
|
|
|
//
|
|
// Bug #340164: on DCs, mark handles opened by
|
|
// anonymous users as such
|
|
//
|
|
|
|
IsAnonymous = TRUE;
|
|
InternalHandle->Options |= LSAP_DB_OPENED_BY_ANONYMOUS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Bug #340164: on non-DCs, prevent anonymous
|
|
// users from opening LSA policy handles
|
|
//
|
|
|
|
static LONGLONG LsapAnonymousRejectedLastLogged = 0;
|
|
LONGLONG CurrentTime;
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
GetSystemTimeAsFileTime(( PFILETIME )&CurrentTime );
|
|
|
|
if (( CurrentTime - LsapAnonymousRejectedLastLogged ) >= ( 24i64 * 60 * 60 * 10 * 1000 * 1000 )) {
|
|
|
|
LsapAnonymousRejectedLastLogged = CurrentTime;
|
|
MustLogAnonymousAccessDenied = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NtClose( ClientToken );
|
|
}
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
if( NT_SUCCESS( Status ) ) {
|
|
|
|
if( NULL != MappingTableToUse ) {
|
|
|
|
Status = NtAccessCheckByTypeResultListAndAuditAlarm(
|
|
&LsapState.SubsystemName,
|
|
ObjectHandle,
|
|
&LsapDbObjectTypeNames[ObjectTypeId],
|
|
( PUNICODE_STRING )ObjectInformation->ObjectAttributes.ObjectName,
|
|
ObjectInformation->ObjectAttributes.SecurityDescriptor,
|
|
NULL, // Principal Self sid
|
|
MAXIMUM_ALLOWED |
|
|
((DesiredAccess & ACCESS_SYSTEM_SECURITY )?
|
|
ACCESS_SYSTEM_SECURITY:0),
|
|
AuditEventObjectAccess,
|
|
0, // FLAGS
|
|
TypeListToCheck,
|
|
TypeListToCheckCount,
|
|
&LsapDsGenericMap,
|
|
FALSE,
|
|
GrantedAccessArray,
|
|
AccessStatusArray,
|
|
( PBOOLEAN )&( InternalHandle->GenerateOnClose ) );
|
|
} else {
|
|
|
|
//
|
|
// In Policy object case there are two rights, that must be defered to provide compatibility
|
|
// with previous versions; POLICY_TRUST_ADMIN, READ_CONTROL. Why these have to be defered
|
|
// is explained below;
|
|
//
|
|
|
|
//
|
|
// Don't fail the LsaOpenPolicy just because POLICY_TRUST_ADMIN access is requested
|
|
//
|
|
// NT 4.0 and older required that the caller ask for POLICY_TRUST_ADMIN to manipulate TDOs.
|
|
// That's no longer required, but some applications still ask for it. So, don't fail
|
|
// the LsaOpenPolicy if the access isn't granted. Rather, let the individual call fail
|
|
// when it checks to see if the access was granted on the handle.
|
|
//
|
|
// TDOs access is now controlled by the security descriptor on the TDO. That allows
|
|
// delegation of TDO manipulation.
|
|
//
|
|
|
|
//
|
|
// Don't fail the LsaOpenPolicy just because the anonymous user asked for
|
|
// READ_CONTROL. Some apps ask for GENERIC_EXECUTE which contains READ_CONTROL
|
|
// even though the app really doesn't need it.
|
|
//
|
|
ULONG RequestedAccess = DesiredAccess;
|
|
ULONG DeferAccess[] = {
|
|
0,
|
|
POLICY_TRUST_ADMIN,
|
|
READ_CONTROL,
|
|
POLICY_TRUST_ADMIN | READ_CONTROL
|
|
};
|
|
ULONG i;
|
|
ULONG NumberOfIterations = ( ObjectTypeId == PolicyObject ? RTL_NUMBER_OF( DeferAccess ) : 1 );
|
|
|
|
for( i = 0; i < NumberOfIterations; ++i ) {
|
|
|
|
if( ObjectTypeId == PolicyObject ) {
|
|
|
|
//
|
|
// If the access to be defered is not asked for
|
|
// then just skip this.
|
|
//
|
|
if( ( RequestedAccess & DeferAccess[i] ) != DeferAccess[i] ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If the access to be defered contains READ_CONTROL, and
|
|
// the user is not anonymous then skip it since READ_CONTROL
|
|
// must be defered only anonymous case
|
|
//
|
|
if( FLAG_ON( DeferAccess[i], READ_CONTROL ) &&
|
|
!IsAnonymous ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
DesiredAccess = RequestedAccess & ~DeferAccess[i];
|
|
}
|
|
|
|
//
|
|
// Because of bug 411289, the NtAccessCheck* API's don't return
|
|
// ACCESS_DENIED when presented with 0 access. Because clients
|
|
// may already expect this behavoir, only return ACCESS_DENIED
|
|
// when the client really doesn't have any access. We want to
|
|
// return ACCESS_DENIED to prevent anonymous clients from acquiring
|
|
// handles.
|
|
//
|
|
if ( DesiredAccess == 0 ) {
|
|
fNoAccessRequested = TRUE;
|
|
DesiredAccess = MAXIMUM_ALLOWED;
|
|
}
|
|
|
|
Status = NtAccessCheckByTypeAndAuditAlarm(
|
|
&LsapState.SubsystemName,
|
|
ObjectHandle,
|
|
&LsapDbObjectTypeNames[ObjectTypeId],
|
|
( PUNICODE_STRING )ObjectInformation->ObjectAttributes.ObjectName,
|
|
ObjectInformation->ObjectAttributes.SecurityDescriptor,
|
|
NULL, // Principal Self sid
|
|
DesiredAccess,
|
|
AuditEventObjectAccess,
|
|
0, // FLAGS
|
|
TypeListToCheck,
|
|
TypeListToCheckCount,
|
|
&( LsapDbState.DbObjectTypes[ ObjectTypeId ].GenericMapping ),
|
|
FALSE,
|
|
( PACCESS_MASK )&GrantedAccess ,
|
|
( PNTSTATUS )&AccessStatus,
|
|
( PBOOLEAN )&( InternalHandle->GenerateOnClose ) );
|
|
|
|
if ( fNoAccessRequested ) {
|
|
|
|
DesiredAccess = 0;
|
|
|
|
if ( NT_SUCCESS( Status )
|
|
&& NT_SUCCESS( AccessStatus ) ) {
|
|
GrantedAccess = 0;
|
|
}
|
|
}
|
|
|
|
if( !NT_SUCCESS( Status ) ||
|
|
AccessStatus != STATUS_ACCESS_DENIED ) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) ){
|
|
ULONG i,j;
|
|
|
|
|
|
switch ( InternalHandle->ObjectTypeId ) {
|
|
case SecretObject:
|
|
case TrustedDomainObject:
|
|
|
|
//
|
|
// If we are running on a client, or local secret No need to do any mapping
|
|
//
|
|
|
|
if (NULL==MappingTableToUse) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Take standard rights from the object guid level in the type list
|
|
//
|
|
|
|
GrantedAccess = 0;
|
|
if (NT_SUCCESS(AccessStatusArray[0]))
|
|
{
|
|
GrantedAccess |= (GrantedAccessArray[0]) & ((STANDARD_RIGHTS_ALL)|ACCESS_SYSTEM_SECURITY);
|
|
}
|
|
|
|
for (i=0;i<TypeListToCheckCount;i++)
|
|
{
|
|
if (NT_SUCCESS(AccessStatusArray[i]))
|
|
{
|
|
fAtLeastOneAccessGranted = TRUE;
|
|
|
|
for (j=0;j<MappingTableSize;j++)
|
|
{
|
|
if ((0==memcmp(MappingTableToUse[j].ObjectGuid,TypeListToCheck[i].ObjectType,sizeof(GUID)))
|
|
&& (GrantedAccessArray[i] & MappingTableToUse[j].DsAccessRequired))
|
|
{
|
|
//
|
|
// Or in the downlevel right granted by virtue of the granted DS right
|
|
// on the particular prop set guid
|
|
//
|
|
|
|
GrantedAccess |= MappingTableToUse[j].DesiredAccess;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Grant in the unused trusted domain access bits
|
|
//
|
|
|
|
if (TrustedDomainObject == InternalHandle->ObjectTypeId)
|
|
{
|
|
GrantedAccess |=TRUSTED_QUERY_CONTROLLERS|TRUSTED_SET_CONTROLLERS;
|
|
}
|
|
|
|
if ( !fAtLeastOneAccessGranted )
|
|
{
|
|
// No access' granted
|
|
GrantedAccess = 0;
|
|
AccessStatus = STATUS_ACCESS_DENIED;
|
|
}
|
|
else if (DesiredAccess & MAXIMUM_ALLOWED)
|
|
{
|
|
//
|
|
// Granted access already contains the maximum allowed access in lsa terms as
|
|
// computed above
|
|
//
|
|
|
|
AccessStatus = STATUS_SUCCESS;
|
|
|
|
}
|
|
else if (RtlAreAllAccessesGranted(GrantedAccess,DesiredAccess))
|
|
{
|
|
GrantedAccess = DesiredAccess;
|
|
AccessStatus = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// One or more accesses that were requested was not granted
|
|
//
|
|
|
|
GrantedAccess = 0;
|
|
AccessStatus = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
if (NT_SUCCESS(AccessStatus))
|
|
{
|
|
InternalHandle->GrantedAccess = GrantedAccess;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Check to see whether this is a network request that is coming in or
|
|
// not
|
|
//
|
|
if ( NT_SUCCESS( Status ) && NT_SUCCESS( AccessStatus ) &&
|
|
InternalHandle->ObjectTypeId == SecretObject ) {
|
|
|
|
Status = LsapDbIsRpcClientNetworkClient( &InternalHandle->NetworkClient );
|
|
}
|
|
|
|
#ifdef LSAP_TRACK_HANDLE
|
|
//
|
|
// If we haven't already done so, open the client token so we can copy it off below
|
|
//
|
|
if ( !InternalHandle->ClientToken ) {
|
|
|
|
Status = NtOpenThreadToken( NtCurrentThread(),
|
|
TOKEN_QUERY | TOKEN_DUPLICATE,
|
|
TRUE,
|
|
&ClientToken );
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// Before checking the Status, stop impersonating the client and become
|
|
// our former self.
|
|
//
|
|
|
|
if ( Options & LSAP_DB_USE_LPC_IMPERSONATE ) {
|
|
RevertToSelf();
|
|
RevertStatus = STATUS_SUCCESS;
|
|
} else {
|
|
RevertStatus = I_RpcMapWin32Status(RpcRevertToSelf());
|
|
|
|
if (!NT_SUCCESS(RevertStatus)) {
|
|
|
|
LsapLogError(
|
|
"LsapDbRequestAccessObject: RpcRevertToSelf failed 0x%lx\n",
|
|
RevertStatus
|
|
);
|
|
}
|
|
}
|
|
|
|
#ifdef LSAP_TRACK_HANDLE
|
|
|
|
//
|
|
// Copy off the client token
|
|
//
|
|
if ( ClientToken ) {
|
|
|
|
OBJECT_ATTRIBUTES ObjAttrs;
|
|
SECURITY_QUALITY_OF_SERVICE SecurityQofS;
|
|
|
|
//
|
|
// Duplicate the token
|
|
//
|
|
InitializeObjectAttributes( &ObjAttrs, NULL, 0L, NULL, NULL );
|
|
SecurityQofS.Length = sizeof( SECURITY_QUALITY_OF_SERVICE );
|
|
SecurityQofS.ImpersonationLevel = SecurityImpersonation;
|
|
SecurityQofS.ContextTrackingMode = FALSE; // Snapshot client context
|
|
SecurityQofS.EffectiveOnly = FALSE;
|
|
ObjAttrs.SecurityQualityOfService = &SecurityQofS;
|
|
|
|
Status = NtDuplicateToken( ClientToken,
|
|
TOKEN_READ | TOKEN_WRITE | TOKEN_EXECUTE,
|
|
&ObjAttrs,
|
|
FALSE,
|
|
TokenImpersonation,
|
|
&InternalHandle->ClientToken );
|
|
|
|
|
|
NtClose( ClientToken );
|
|
}
|
|
#endif
|
|
|
|
if ( MustLogAnonymousAccessDenied ) {
|
|
|
|
//
|
|
// REVIEW: replace the foregoing by a call to LsapGetClientNetworkAddress
|
|
//
|
|
|
|
RPC_STATUS Local;
|
|
UNICODE_STRING Name = {0};
|
|
RPC_BINDING_HANDLE ServerBinding = NULL;
|
|
|
|
Local = RpcBindingServerFromClient(
|
|
NULL, // in context of an RPC call, client binding not required
|
|
&ServerBinding
|
|
);
|
|
|
|
if ( Local == RPC_S_OK ) {
|
|
|
|
WCHAR * StringBinding = NULL;
|
|
|
|
Local = RpcBindingToStringBindingW(
|
|
ServerBinding,
|
|
&StringBinding
|
|
);
|
|
|
|
if ( Local == RPC_S_OK ) {
|
|
|
|
Local = RpcStringBindingParseW(
|
|
StringBinding,
|
|
NULL, // don't care about UUID
|
|
NULL, // don't care about ProtSeq
|
|
&Name.Buffer,
|
|
NULL, // don't care about EndPoint
|
|
NULL // don't care about NetworkOptions
|
|
);
|
|
|
|
if ( Local == RPC_S_OK ) {
|
|
|
|
Name.Length = wcslen( Name.Buffer ) * sizeof( WCHAR );
|
|
Name.MaximumLength = Name.Length + sizeof( WCHAR );
|
|
}
|
|
}
|
|
|
|
if ( StringBinding != NULL ) {
|
|
|
|
RpcStringFreeW( &StringBinding );
|
|
}
|
|
}
|
|
|
|
SpmpReportEventU(
|
|
EVENTLOG_ERROR_TYPE,
|
|
LSA_OPEN_POLICY_BY_ANONYMOUS_REJECTED,
|
|
0,
|
|
0,
|
|
NULL,
|
|
1,
|
|
&Name
|
|
);
|
|
|
|
if ( Name.Buffer != NULL ) {
|
|
|
|
RpcStringFreeW( &Name.Buffer );
|
|
}
|
|
|
|
if ( ServerBinding != NULL ) {
|
|
|
|
RpcBindingFree( ServerBinding );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the primary status code is a success status code, return the
|
|
// secondary status code. If this is alsoa success code, return the
|
|
// revert to self status.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = AccessStatus;
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = RevertStatus;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
LsapDbRequestAccessNewObject(
|
|
IN OUT LSAPR_HANDLE ObjectHandle,
|
|
IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG Options
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function verifies that a desired set of accesses can be granted
|
|
to the handle that is opened when a new object is created.
|
|
|
|
It is important to note that the rules for granting accesses to the
|
|
handle that is open upon object creation are different from the rules
|
|
for granting accesses upon the opening of an existing object. For a new
|
|
object, the associated handle will be granted any subset of GENERIC_ALL
|
|
access desired and, if the creator has SE_SECURITY_PRIVILEGE, the handle
|
|
will be granted ACCESS_SYSTEM_SECURITY access if requested. If the
|
|
creator requests MAXIMUM_ALLOWED, the handle will be granted GENERIC_ALL.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Handle to object. The handle will receive the
|
|
granted accesses if the call is successful.
|
|
|
|
ObjectInformation - Pointer to object's information. As a minimum, the
|
|
object's Security Descriptor must be set up.
|
|
|
|
DesiredAccess - Specifies a mask of the access types desired to the
|
|
object.
|
|
|
|
Options - Specifies optional actions to be taken
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Not all of the Desired Accessed can be
|
|
granted to the caller.
|
|
|
|
STATUS_BACKUP_CONTROLLER - A create, update or delete operation
|
|
is not allowed for a non-trusted client for this object on a BDC,
|
|
because the object is global to all DC's for a domain and is replicated.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ACCESS_MASK EffectiveDesiredAccess = DesiredAccess;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
|
|
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = InternalHandle->ObjectTypeId;
|
|
ULONG EffectiveOptions = Options | InternalHandle->Options;
|
|
|
|
#ifndef LSADS_MAP_SD
|
|
if ( LsapDsIsWriteDs( ObjectHandle ) ) {
|
|
|
|
LsapDsDebugOut(( DEB_TRACE, "Shortcurcuit Ds access check\n" ));
|
|
InternalHandle->GrantedAccess = 0xFFFFFFFF;
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If MAXIMUM_ALLOWED is requested, add GENERIC_ALL
|
|
//
|
|
|
|
if (EffectiveDesiredAccess & MAXIMUM_ALLOWED) {
|
|
|
|
EffectiveDesiredAccess |= GENERIC_ALL;
|
|
}
|
|
|
|
//
|
|
// If ACCESS_SYSTEM_SECURITY is requested and we are a non-trusted
|
|
// client, check that we have SE_SECURITY_PRIVILEGE.
|
|
//
|
|
|
|
if ((EffectiveDesiredAccess & ACCESS_SYSTEM_SECURITY) &&
|
|
(!InternalHandle->Trusted)) {
|
|
|
|
Status = LsapRtlWellKnownPrivilegeCheck(
|
|
(PVOID)ObjectHandle,
|
|
SE_SECURITY_PRIVILEGE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto RequestAccessNewObjectError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure the caller can be given the requested access
|
|
// to the new object
|
|
//
|
|
|
|
InternalHandle->GrantedAccess = EffectiveDesiredAccess;
|
|
|
|
RtlMapGenericMask(
|
|
&InternalHandle->GrantedAccess,
|
|
&LsapDbState.DbObjectTypes[ObjectTypeId].GenericMapping
|
|
);
|
|
|
|
if ((LsapDbState.DbObjectTypes[ObjectTypeId].InvalidMappedAccess
|
|
&InternalHandle->GrantedAccess) != 0) {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto RequestAccessNewObjectError;
|
|
}
|
|
|
|
RequestAccessNewObjectFinish:
|
|
|
|
return(Status);
|
|
|
|
RequestAccessNewObjectError:
|
|
|
|
goto RequestAccessNewObjectFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbCloseObject (
|
|
IN PLSAPR_HANDLE ObjectHandle,
|
|
IN ULONG Options,
|
|
IN NTSTATUS PreliminaryStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function closes (dereferences) a handle to an Lsa Database object.
|
|
If the reference count of the handle reduces to 0, the handle is freed.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Pointer to handle to object from LsapDbOpenObject or
|
|
LsapDbCreateObject.
|
|
|
|
Options - Optional actions to be performed
|
|
|
|
LSAP_DB_VALIDATE_HANDLE - Verify that the handle is valid.
|
|
|
|
LSAP_DB_DEREFERENCE_CONTR - Dereference the Container Handle. Note
|
|
that the Container Handle was referenced when the subordinate
|
|
handle was created.
|
|
|
|
LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES - Permit the handle provided
|
|
to be for a deleted object.
|
|
|
|
PreliminaryStatus - Used to decide whether to abort or commit the transaction
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Dereference the object handle and free the handle if the reference count
|
|
// reaches zero. Optionally, the handle will be verified and/or freed
|
|
// and the container object handle dereferenced.
|
|
//
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
ObjectHandle,
|
|
NullObject,
|
|
NullObject,
|
|
Options,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
PreliminaryStatus
|
|
);
|
|
|
|
*ObjectHandle = NULL;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbDeleteObject(
|
|
IN LSAPR_HANDLE ObjectHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function deletes an object from the Lsa Database.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Handle to open object to be deleted.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard NT Result Code.
|
|
|
|
STATUS_INVALID_HANDLE - Handle is not a valid handle to an open
|
|
object.
|
|
|
|
STATUS_ACCESS_DENIED - Handle does not specify DELETE access.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) ObjectHandle;
|
|
PUNICODE_STRING AttributeNames[LSAP_DB_MAX_ATTRIBUTES];
|
|
BOOLEAN DeleteSecurely[LSAP_DB_MAX_ATTRIBUTES] = {0};
|
|
PUNICODE_STRING *NextAttributeName;
|
|
LSAP_DB_ATTRIBUTE Attributes[ 2 ];
|
|
ULONG AttributeCount;
|
|
ULONG AttributeNumber;
|
|
LSAPR_TRUST_INFORMATION TrustInformation;
|
|
BOOLEAN TrustedDomainCacheIsUpToDate = TRUE;
|
|
|
|
|
|
//
|
|
// All object types have a Security Descriptor stored as the SecDesc
|
|
// attribute.
|
|
//
|
|
|
|
NextAttributeName = AttributeNames;
|
|
AttributeCount = 0;
|
|
*NextAttributeName = &LsapDbNames[SecDesc];
|
|
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Check the other references to the object and mark all other handles
|
|
// invalid.
|
|
//
|
|
|
|
Status = LsapDbMarkDeletedObjectHandles( ObjectHandle, FALSE );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteObjectError;
|
|
}
|
|
|
|
//
|
|
// Switch on object type
|
|
//
|
|
|
|
switch (Handle->ObjectTypeId) {
|
|
|
|
case PolicyObject:
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
case TrustedDomainObject:
|
|
|
|
//
|
|
// Deal with the TrustedDomainAsSecret problem
|
|
if (FLAG_ON( Handle->Options, LSAP_DB_DS_TRUSTED_DOMAIN_AS_SECRET ) ) {
|
|
|
|
LsapDbInitializeAttributeDs( &Attributes[ 0 ],
|
|
TrDmSAI,
|
|
NULL,
|
|
0,
|
|
FALSE );
|
|
|
|
LsapDbInitializeAttributeDs( &Attributes[ 1 ],
|
|
TrDmSAO,
|
|
NULL,
|
|
0,
|
|
FALSE );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Check for delegatable trust deletion quotas
|
|
//
|
|
|
|
if ( LsapDsIsHandleDsHandle( ObjectHandle )) {
|
|
Status = LsapCheckTDODeletionQuotas(ObjectHandle);
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
*NextAttributeName = &LsapDbNames[TrDmName];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[Sid];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[TrDmAcN];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[TrDmCtN];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[TrDmPxOf];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[TrDmCtEn];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
//
|
|
// Delete the object from the list of Trusted Domains
|
|
//
|
|
|
|
TrustInformation.Sid = Handle->Sid;
|
|
TrustInformation.Name = *((PLSAPR_UNICODE_STRING) &Handle->LogicalNameU);
|
|
|
|
Status = LsapDbAcquireWriteLockTrustedDomainList();
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
Status = LsapDbDeleteTrustedDomainList( &TrustInformation );
|
|
|
|
TrustedDomainCacheIsUpToDate = FALSE;
|
|
LsapDbReleaseLockTrustedDomainList();
|
|
}
|
|
|
|
//
|
|
// Then, delete the sam account corresponding to this trust, if it existed
|
|
//
|
|
Status = LsapDsDeleteInterdomainTrustAccount( ObjectHandle );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AccountObject:
|
|
|
|
*NextAttributeName = &LsapDbNames[Sid];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[ActSysAc];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[Privilgs];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
//
|
|
// BUG 667716: QuotaLim appears to be dead code, but it is not.
|
|
// Accounts on machines upgraded from NT4 may have this
|
|
// value set on them, in which case they will not be
|
|
// deleted properly unless QuotaLim is deleted also,
|
|
// leading to LSA account database corruption and
|
|
// inability to enumerate accounts and replicate to DCs
|
|
//
|
|
|
|
*NextAttributeName = &LsapDbNames[QuotaLim];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
break;
|
|
|
|
case SecretObject:
|
|
|
|
DeleteSecurely[AttributeCount] = TRUE;
|
|
*NextAttributeName = &LsapDbNames[CurrVal];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
DeleteSecurely[AttributeCount] = TRUE;
|
|
*NextAttributeName = &LsapDbNames[OldVal];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[CupdTime];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[OupdTime];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
break;
|
|
|
|
default:
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteObjectError;
|
|
}
|
|
|
|
//
|
|
// Add Registry Transactions to delete each of the object's attributes.
|
|
//
|
|
for(AttributeNumber = 0;
|
|
AttributeNumber < AttributeCount && !LsapDsIsHandleDsHandle( ObjectHandle);
|
|
AttributeNumber++) {
|
|
|
|
Status = LsapDbDeleteAttributeObject(
|
|
ObjectHandle,
|
|
AttributeNames[AttributeNumber],
|
|
DeleteSecurely[AttributeNumber]
|
|
);
|
|
|
|
//
|
|
// Ignore "attribute not found" errors. The object need not
|
|
// have all attributes set, or may be only partially created.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
break;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteObjectError;
|
|
}
|
|
|
|
//
|
|
// Close the handle to the Registry Key representing the object.
|
|
// The Registry transaction package will open another handle with
|
|
// DELETE access to perform the deletion.
|
|
//
|
|
if ( !LsapDsIsHandleDsHandle( ObjectHandle) &&
|
|
Handle->KeyHandle != NULL ) {
|
|
|
|
Status = NtClose(Handle->KeyHandle);
|
|
|
|
Handle->KeyHandle = NULL;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteObjectError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add a Registry Transaction to delete the object's Registry Key.
|
|
//
|
|
|
|
if ( !LsapDsIsHandleDsHandle( ObjectHandle) ) {
|
|
|
|
Status = LsapRegDeleteObject( &Handle->PhysicalNameU );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Deal with the special case of TrustedDomainObject as Secret
|
|
//
|
|
if ( FLAG_ON( Handle->Options, LSAP_DB_DS_TRUSTED_DOMAIN_AS_SECRET ) &&
|
|
Handle->ObjectTypeId == TrustedDomainObject) {
|
|
|
|
Status = LsapDsDeleteAttributes( &Handle->PhysicalNameDs,
|
|
Attributes,
|
|
2 );
|
|
} else {
|
|
|
|
Status = LsapDsDeleteObject( &Handle->PhysicalNameDs );
|
|
}
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteObjectError;
|
|
}
|
|
|
|
TrustedDomainCacheIsUpToDate = TRUE;
|
|
|
|
//
|
|
// If the machine account is being deleted, make sure to do notification on it
|
|
//
|
|
if ( Handle->ObjectTypeId == SecretObject && LsapDbSecretIsMachineAcc( Handle ) ) {
|
|
|
|
LsaINotifyChangeNotification( PolicyNotifyMachineAccountPasswordInformation );
|
|
}
|
|
|
|
DeleteObjectFinish:
|
|
|
|
if( !TrustedDomainCacheIsUpToDate ) {
|
|
|
|
if( NT_SUCCESS( LsapDbAcquireWriteLockTrustedDomainList() ) ) {
|
|
|
|
LsapDbMakeCacheInvalid( TrustedDomainObject );
|
|
LsapDbReleaseLockTrustedDomainList();
|
|
}
|
|
}
|
|
//
|
|
// Decrement the count of objects of the given type.
|
|
//
|
|
|
|
LsapDbDecrementCountObject(
|
|
((LSAP_DB_HANDLE) ObjectHandle)->ObjectTypeId
|
|
);
|
|
|
|
return (Status);
|
|
|
|
DeleteObjectError:
|
|
|
|
goto DeleteObjectFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbReferenceObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN LSAP_DB_OBJECT_TYPE_ID HandleTypeId,
|
|
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
|
|
IN ULONG Options
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function verifies that a passed handle is valid, is for an
|
|
object of the specified type and has the specified accesses granted.
|
|
The handle's reference count is then incremented. If Lsa Database
|
|
locking is not requested, the Lsa Database must aready be locked.
|
|
If Lsa Database locking is requested, the Lsa Database must NOT be
|
|
locked.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Pointer to handle to be validated and referenced.
|
|
|
|
DesiredAccess - Specifies the accesses that are desired. The function
|
|
returns an error if any of the specified accesses have not been
|
|
granted.
|
|
|
|
HandleTypeId - Specifies the expected object type to which the handle
|
|
relates. An error is returned if this type does not match the
|
|
type contained in the handle.
|
|
|
|
ObjectTypeId - Specifies the expected object type to which the object that is to
|
|
be operated upon relates
|
|
|
|
Options - Specifies optional additional actions including database state
|
|
changes to be made, or actions not to be performed.
|
|
|
|
LSAP_DB_LOCK - Acquire the Lsa database lock. If this
|
|
flag is specified, the Lsa Database must NOT already be locked.
|
|
If this flag is not specified, the Lsa Database must already
|
|
be locked.
|
|
|
|
LSAP_DB_LOG_QUEUE_LOCK - Acquire the Lsa Audit Log Queue
|
|
Lock.
|
|
|
|
LSAP_DB_START_TRANSACTION - Start an Lsa database transaction.
|
|
|
|
NOTE: There may be some Options (not database states) provided in the
|
|
ObjectHandle. These options augment those provided.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_INVALID_HANDLE - The handle could not be found.
|
|
|
|
STATUS_ACCESS_DENIED - Not all of the accesses specified have
|
|
been granted.
|
|
|
|
STATUS_OBJECT_TYPE_MISMATCH - The specified object type id does not
|
|
match the object type id contained in the handle.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources to
|
|
complete the command. An example is too many references to
|
|
the handle causing the count to overflow.
|
|
|
|
STATUS_BACKUP_CONTROLLER - A request to open a transaction has been
|
|
made by a non-trusted caller and the system is a Backup Domain
|
|
Controller. The LSA Database of a Backup Domain Controller
|
|
can only be updated by a trusted client, such as a replicator.
|
|
|
|
Result Codes from database transaction package.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
|
|
BOOLEAN GlobalSecret = FALSE;
|
|
ULONG States, EffectiveOptions;
|
|
ULONG ResetStates = 0;
|
|
BOOLEAN HandleReferenced = FALSE;
|
|
BOOLEAN StatesSet = FALSE;
|
|
|
|
LsapDsDebugOut(( DEB_FTRACE, "LsapDbReferenceObject\n" ));
|
|
|
|
//
|
|
// Search the list of handles for the given handle, validate the
|
|
// handle and verify that is for an object of the expected type.
|
|
// Augment the options passed in with those contained in the handle.
|
|
//
|
|
// Reference the handle.
|
|
//
|
|
|
|
Status = LsapDbVerifyHandle( ObjectHandle, 0, HandleTypeId, TRUE );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ObjectHandle = NULL;
|
|
goto ReferenceError;
|
|
}
|
|
|
|
HandleReferenced = TRUE;
|
|
|
|
|
|
States = Options & LSAP_DB_STATE_MASK;
|
|
|
|
//
|
|
// Set the requested states before doing anything else. This ensures
|
|
// that the validity checks performed by this function are performed
|
|
// while the Lsa database is locked.
|
|
//
|
|
ResetStates |= ( States & ( LSAP_DB_READ_ONLY_TRANSACTION |
|
|
LSAP_DB_DS_OP_TRANSACTION ) );
|
|
|
|
if (States != 0) {
|
|
|
|
Status = LsapDbSetStates( States,
|
|
ObjectHandle,
|
|
ObjectTypeId );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
|
|
goto ReferenceError;
|
|
}
|
|
|
|
StatesSet = TRUE;
|
|
|
|
if (States & LSAP_DB_START_TRANSACTION) {
|
|
|
|
ResetStates |= LSAP_DB_FINISH_TRANSACTION;
|
|
}
|
|
|
|
if (States & LSAP_DB_LOCK) {
|
|
|
|
ResetStates |= LSAP_DB_LOCK;
|
|
}
|
|
|
|
if (States & LSAP_DB_LOG_QUEUE_LOCK) {
|
|
|
|
ResetStates |= LSAP_DB_LOG_QUEUE_LOCK;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// There may also be options set in the handle. Take these into
|
|
// account as well.
|
|
//
|
|
|
|
EffectiveOptions = Options | InternalHandle->Options;
|
|
|
|
//
|
|
// If the handle is not Trusted, verify that the desired accesses have been granted
|
|
//
|
|
|
|
if (!(InternalHandle->Trusted)) {
|
|
|
|
if (!RtlAreAllAccessesGranted( InternalHandle->GrantedAccess, DesiredAccess )) {
|
|
|
|
//
|
|
// Check to see if the caller is using authenticated RPC & they
|
|
// are failing because of POLICY_LOOKUP_NAME access
|
|
//
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
if ((InternalHandle->ObjectTypeId == PolicyObject) &&
|
|
RtlAreAllAccessesGranted(POLICY_LOOKUP_NAMES,DesiredAccess) &&
|
|
!RtlAreAllAccessesGranted(InternalHandle->GrantedAccess, POLICY_LOOKUP_NAMES) ) {
|
|
|
|
ULONG RpcErr;
|
|
ULONG AuthnLevel = 0;
|
|
ULONG AuthnSvc = 0;
|
|
|
|
RpcErr = RpcBindingInqAuthClient(
|
|
NULL,
|
|
NULL, // no privileges
|
|
NULL, // no server principal name
|
|
&AuthnLevel,
|
|
&AuthnSvc,
|
|
NULL // no authorization level
|
|
);
|
|
//
|
|
// If it as authenticated at level packet integrity or better
|
|
// and is done with the netlogon package, allow it through
|
|
//
|
|
|
|
if ((RpcErr == ERROR_SUCCESS) &&
|
|
(AuthnLevel >= RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) &&
|
|
(AuthnSvc == RPC_C_AUTHN_NETLOGON )) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ReferenceError;
|
|
}
|
|
}
|
|
}
|
|
|
|
LsapDsDebugOut(( DEB_FTRACE, "LsapDbReferenceObject: 0x%lx\n", Status ));
|
|
|
|
return (Status);
|
|
|
|
ReferenceError:
|
|
|
|
//
|
|
// Unset the states in the correct order. If a database transaction
|
|
// was started by this routine, it will be aborted. Ignore the status
|
|
// No registry transaction can be active here
|
|
//
|
|
if (( ObjectHandle != NULL ) && (StatesSet)) {
|
|
LsapDbResetStates( ObjectHandle,
|
|
ResetStates,
|
|
ObjectTypeId,
|
|
( SECURITY_DB_DELTA_TYPE )0,
|
|
Status );
|
|
}
|
|
|
|
//
|
|
// Dereference the handle if we referenced it.
|
|
//
|
|
|
|
if ( HandleReferenced ) {
|
|
LsapDbDereferenceHandle( ObjectHandle, FALSE );
|
|
}
|
|
|
|
LsapDsDebugOut(( DEB_FTRACE, "LsapDbReferenceObject: 0x%lx\n", Status ));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbDereferenceObject(
|
|
IN OUT PLSAPR_HANDLE ObjectHandle,
|
|
IN LSAP_DB_OBJECT_TYPE_ID HandleTypeId,
|
|
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
|
|
IN ULONG Options,
|
|
IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType,
|
|
IN NTSTATUS PreliminaryStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function dereferences a handle, optionally validating it first.
|
|
If the Reference Count in the handle goes to 0, the handle is freed.
|
|
The Lsa Database may optionally be unlocked by this function. It
|
|
must be locked before calling this function.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Pointer to handle to be dereferenced. If the reference
|
|
count reaches 0, NULL is returned in this location.
|
|
|
|
HandleTypeId - Specifies the expected object type to which the handle
|
|
relates. An error is returned if this type does not match the
|
|
type contained in the handle.
|
|
|
|
ObjectTypeId - Specifies the expected object type to which the object that is to
|
|
be operated upon relates
|
|
|
|
Options - Specifies optional additional actions to be performed including
|
|
Lsa Database states to be cleared.
|
|
|
|
LSAP_DB_VALIDATE_HANDLE - Validate the handle.
|
|
|
|
LSAP_DEREFERENCE_CONTR - Dereference the container object
|
|
|
|
LSAP_DB_FINISH_TRANSACTION - A database transaction was started
|
|
and must be concluded. Conclude the current Lsa Database
|
|
transaction by applying or aborting it depending on the
|
|
final Status.
|
|
|
|
LSAP_DB_LOCK - The Lsa database lock was acquired and
|
|
should be released.
|
|
|
|
LSAP_DB_LOG_QUEUE_LOCK - The Lsa Audit Log Queue Lock
|
|
was acquired and should be released.
|
|
|
|
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit notification to
|
|
Replicator of the change.
|
|
|
|
LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES - Permit the handle provided
|
|
to be for a deleted object.
|
|
|
|
NOTE: There may be some Options (not database states) provided in the
|
|
ObjectHandle. These options augment those provided.
|
|
|
|
PreliminaryStatus = Current Result Code.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_INVALID_HANDLE - The handle could not be found.
|
|
|
|
STATUS_OBJECT_TYPE_MISMATCH - The specified object type id does not
|
|
match the object type id contained in the handle.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, SecondaryStatus, TmpStatus;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) *ObjectHandle;
|
|
BOOLEAN DecrementCount = TRUE;
|
|
ULONG EffectiveOptions;
|
|
ULONG ReferenceCount = 0;
|
|
|
|
LsapDsDebugOut(( DEB_FTRACE, "LsapDbDereferenceObject\n" ));
|
|
|
|
Status = PreliminaryStatus;
|
|
SecondaryStatus = STATUS_SUCCESS;
|
|
|
|
|
|
//
|
|
// There may also be options set in the handle. Take these into
|
|
// account as well.
|
|
//
|
|
|
|
EffectiveOptions = Options | InternalHandle->Options;
|
|
|
|
//
|
|
// If validating, lookup the handle and match the type.
|
|
//
|
|
|
|
if (EffectiveOptions & LSAP_DB_VALIDATE_HANDLE) {
|
|
|
|
SecondaryStatus = LsapDbVerifyHandle(
|
|
*ObjectHandle,
|
|
EffectiveOptions,
|
|
HandleTypeId,
|
|
FALSE );
|
|
|
|
if (!NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
DecrementCount = FALSE;
|
|
goto DereferenceObjectError;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Dereference the container handle if so requested
|
|
//
|
|
|
|
if (EffectiveOptions & LSAP_DB_DEREFERENCE_CONTR) {
|
|
|
|
if (InternalHandle->ContainerHandle != NULL) {
|
|
//
|
|
// Dereference the container object.
|
|
//
|
|
|
|
SecondaryStatus = LsapDbDereferenceObject(
|
|
(PLSAPR_HANDLE) &InternalHandle->ContainerHandle,
|
|
NullObject,
|
|
NullObject,
|
|
LSAP_DB_READ_ONLY_TRANSACTION |
|
|
LSAP_DB_DS_OP_TRANSACTION |
|
|
LSAP_DB_STANDALONE_REFERENCE,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
STATUS_SUCCESS
|
|
);
|
|
}
|
|
}
|
|
|
|
DereferenceObjectFinish:
|
|
|
|
//
|
|
// Unlock the database.
|
|
//
|
|
|
|
if (NT_SUCCESS(SecondaryStatus))
|
|
{
|
|
SecondaryStatus = LsapDbResetStates(
|
|
*ObjectHandle,
|
|
EffectiveOptions,
|
|
ObjectTypeId,
|
|
SecurityDbDeltaType,
|
|
Status
|
|
);
|
|
}
|
|
|
|
//
|
|
// Dereference the object handle
|
|
//
|
|
|
|
if ( DecrementCount ) {
|
|
|
|
//
|
|
// If the reference count reached zero,
|
|
// ditch to callers pointer to the handle.
|
|
//
|
|
if ( LsapDbDereferenceHandle( *ObjectHandle, NT_SUCCESS( Status ) ) ) {
|
|
|
|
*ObjectHandle = NULL;
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
LsapDsDebugOut(( DEB_FTRACE, "LsapDbDereferenceObject: 0x%lx\n", Status ));
|
|
|
|
return( Status );
|
|
|
|
DereferenceObjectError:
|
|
|
|
if (NT_SUCCESS(Status) && !NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
goto DereferenceObjectFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbReadAttributeObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN PUNICODE_STRING AttributeNameU,
|
|
IN OPTIONAL PVOID AttributeValue,
|
|
IN OUT PULONG AttributeValueLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads the value of an attribute of an open LSA Database object.
|
|
|
|
WARNING: The Lsa Database must be in the locked state when this function
|
|
is called and the supplied ObjectHandle must be valid.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - LSA Handle to object. This must be valid.
|
|
|
|
AttributeNameU - Pointer to Unicode name of attribute
|
|
|
|
AttributeValue - Pointer to buffer to receive attribute's value. This
|
|
parameter may be NULL if the input AttributeValueLength is zero.
|
|
|
|
AttributeValueLength - Pointer to variable containing on input the size of
|
|
attribute value buffer and on output the size of the attributes's
|
|
value. A value of zero may be specified to indicate that the size of
|
|
the attribute's value is unknown.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_BUFFER_OVERFLOW - This warning is returned if the specified
|
|
attribute value length is non-zero and too small for the
|
|
attribute's value.
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// The LSA Database is implemented as a subtree of the Configuration
|
|
// Registry. In this implementation, Lsa Database objects correspond
|
|
// to Registry keys and "attributes" and their "values" correspond to
|
|
// Registry "subkeys" and "values" of the Registry key representing the
|
|
// object.
|
|
//
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Verify that the LSA Database is locked
|
|
// (The lock for the specified object type must be locked.)
|
|
//
|
|
|
|
// ASSERT (LsapDbIsLocked());
|
|
|
|
|
|
Status = LsapRegReadAttribute( ObjectHandle,
|
|
AttributeNameU,
|
|
AttributeValue,
|
|
AttributeValueLength );
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbReadAttributeObjectEx(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN LSAP_DB_NAMES AttributeIndex,
|
|
IN OPTIONAL PVOID AttributeValue,
|
|
IN OUT PULONG AttributeValueLength,
|
|
IN BOOLEAN CanDefaultToZero
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads the value of an attribute of an open LSA Database object.
|
|
|
|
NOTE: This function obsolesces LsapDbReadAttributeObject
|
|
|
|
WARNING: The Lsa Database must be in the locked state when this function
|
|
is called and the supplied ObjectHandle must be valid.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - LSA Handle to object. This must be valid.
|
|
|
|
AttributeIndex - Index into attribute list of attribute to be read
|
|
|
|
AttributeType - Type of the attribute
|
|
|
|
AttributeValue - Pointer to buffer to receive attribute's value. This
|
|
parameter may be NULL if the input AttributeValueLength is zero.
|
|
|
|
AttributeValueLength - Pointer to variable containing on input the size of
|
|
attribute value buffer and on output the size of the attributes's
|
|
value. A value of zero may be specified to indicate that the size of
|
|
the attribute's value is unknown.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_BUFFER_OVERFLOW - This warning is returned if the specified
|
|
attribute value length is non-zero and too small for the
|
|
attribute's value.
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// The LSA Database is implemented as a subtree of the Configuration
|
|
// Registry. In this implementation, Lsa Database objects correspond
|
|
// to Registry keys and "attributes" and their "values" correspond to
|
|
// Registry "subkeys" and "values" of the Registry key representing the
|
|
// object.
|
|
//
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
LSAP_DB_ATTRIBUTE Attribute;
|
|
|
|
if ( !ARGUMENT_PRESENT( AttributeValueLength ) ) {
|
|
|
|
return( STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
LsapDbInitializeAttributeDs(
|
|
&Attribute,
|
|
AttributeIndex,
|
|
AttributeValue,
|
|
AttributeValueLength ? *AttributeValueLength : 0,
|
|
FALSE
|
|
);
|
|
|
|
LsapDbAttributeCanNotExist( &Attribute );
|
|
|
|
|
|
//
|
|
// Verify that the LSA Database is locked
|
|
// (The lock for the specific object type must be locked.)
|
|
//
|
|
// ASSERT (LsapDbIsLocked());
|
|
|
|
Status = LsapDbReadAttributesObject( ObjectHandle,
|
|
0,
|
|
&Attribute,
|
|
1 );
|
|
|
|
if ( Status == STATUS_SUCCESS ) {
|
|
|
|
if ( ARGUMENT_PRESENT( AttributeValue ) ) {
|
|
|
|
if ( *AttributeValueLength >= Attribute.AttributeValueLength ) {
|
|
|
|
RtlCopyMemory( AttributeValue,
|
|
Attribute.AttributeValue,
|
|
Attribute.AttributeValueLength );
|
|
|
|
} else {
|
|
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
|
|
}
|
|
}
|
|
|
|
*AttributeValueLength = Attribute.AttributeValueLength;
|
|
|
|
if ( Attribute.MemoryAllocated ) {
|
|
|
|
MIDL_user_free( Attribute.AttributeValue );
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbWriteAttributeObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN PUNICODE_STRING AttributeNameU,
|
|
IN PVOID AttributeValue,
|
|
IN ULONG AttributeValueLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes the value of an attribute of an open LSA Database
|
|
object. A Database transaction must already be open: the write is
|
|
appended to the transaction log.
|
|
|
|
WARNING: The Lsa Database must be in the locked state when this function
|
|
is called.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Lsa Handle of open object.
|
|
|
|
AttributeNameU - Pointer to Unicode string containing the name of the
|
|
attribute whose value is to be written.
|
|
|
|
AttributeValue - Pointer to buffer containing attribute's value. If NULL
|
|
is specified for this parameter, AttributeValueLength must be 0.
|
|
|
|
AttributeValueLength - Contains the size of attribute value buffer to be
|
|
written. 0 may be specified, indicating that the attribute is to be
|
|
deleted.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The attribute was successfully added to the
|
|
transaction log.
|
|
|
|
STATUS_INVALID_PARAMETER - AttributeValue is NULL but the
|
|
AttributeValueLength value is not 0.
|
|
|
|
Errors from the Registry Transaction Package.
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// The LSA Database is implemented as a subtree of the Configuration
|
|
// Registry. In this implementation, Lsa Database objects correspond
|
|
// to Registry keys and "attributes" and their "values" correspond to
|
|
// Registry "subkeys" and "values" of the Registry key representing the
|
|
// object.
|
|
//
|
|
|
|
NTSTATUS Status;
|
|
UNICODE_STRING PhysicalSubKeyNameU;
|
|
|
|
|
|
PhysicalSubKeyNameU.Buffer = NULL;
|
|
|
|
//
|
|
// Verify that the LSA Database is locked
|
|
// (The lock for the specified object type must be locked.)
|
|
//
|
|
|
|
// ASSERT (LsapDbIsLocked());
|
|
|
|
//
|
|
// If the attribute value is null, verify that the AttributeValueLength
|
|
// field is 0.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(AttributeValue)) {
|
|
|
|
if (AttributeValueLength != 0) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto WriteAttributeObjectError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Writing an object attribute's value is more complex than reading
|
|
// one because the Registry Transaction package is called instead of
|
|
// calling the Registry API directly. Since the transaction package
|
|
// expects to perform its own open of the target subkey representing
|
|
// the attribute (when a transaction commit is finally done) using a
|
|
// name relative to the LSA Database Registry Transaction Key (which
|
|
// we call the Physical Name within the LSA Database code). The
|
|
// Registry Key handle contained in the object handle is therefore
|
|
// not used by the present routine. Instead, we need to construct the
|
|
// Physical Name the sub key and pass it together with the LSA Database
|
|
// Registry transaction key handle on the Registry transaction API
|
|
// call. The Physical Name of the subkey is constructed by
|
|
// concatenating the Physical Object Name stored in the object handle
|
|
// with a "\" and the given sub key name.
|
|
//
|
|
|
|
Status = LsapDbLogicalToPhysicalSubKey(
|
|
ObjectHandle,
|
|
&PhysicalSubKeyNameU,
|
|
AttributeNameU
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto WriteAttributeObjectError;
|
|
}
|
|
|
|
//
|
|
// Now log the sub key write as a Registry Transaction
|
|
//
|
|
|
|
Status = LsapRegWriteAttribute(
|
|
&PhysicalSubKeyNameU,
|
|
AttributeValue,
|
|
AttributeValueLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto WriteAttributeObjectError;
|
|
}
|
|
|
|
WriteAttributeObjectFinish:
|
|
|
|
//
|
|
// If necessary, free the Unicode String buffer allocated by
|
|
// LsapDbLogicalToPhysicalSubKey;
|
|
//
|
|
|
|
if (PhysicalSubKeyNameU.Buffer != NULL) {
|
|
|
|
RtlFreeUnicodeString(&PhysicalSubKeyNameU);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
WriteAttributeObjectError:
|
|
|
|
goto WriteAttributeObjectFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbWriteAttributesObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN PLSAP_DB_ATTRIBUTE Attributes,
|
|
IN ULONG AttributeCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes the values of one or more attributes of an open LSA
|
|
Database object. A Database transaction must already be open: the write
|
|
is appended to the transaction log. The attribute names specified are
|
|
assumed to be consistent with the object type and the values supplied
|
|
are assumed to be valid.
|
|
|
|
WARNINGS: The Lsa Database must be in the locked state when this function
|
|
is called.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Lsa Handle of open object.
|
|
|
|
Attributes - Pointer to an array of Attribute Information blocks each
|
|
containing pointers to the attribute's Unicode Name, the value
|
|
to be stored, and the length of the value in bytes.
|
|
|
|
AttributeCount - Count of the attributes to be written, equivalently,
|
|
this is the number of elements of the array pointed to by Attributes.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Index, Options = 0;
|
|
LSAP_DB_HANDLE InternalHandle;
|
|
|
|
if ( !LsapDsIsWriteDs( ObjectHandle ) ) {
|
|
|
|
for(Index = 0; Index < AttributeCount; Index++) {
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
ObjectHandle,
|
|
Attributes[Index].AttributeName,
|
|
Attributes[Index].AttributeValue,
|
|
Attributes[Index].AttributeValueLength
|
|
);
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
InternalHandle = ( LSAP_DB_HANDLE )ObjectHandle;
|
|
|
|
if ( InternalHandle->ObjectTypeId == SecretObject &&
|
|
FLAG_ON( InternalHandle->Options, LSAP_DB_HANDLE_CREATED_SECRET ) ) {
|
|
|
|
Options |= LSAPDS_REPL_CHANGE_URGENTLY;
|
|
}
|
|
|
|
Status = LsapDsWriteAttributes( &((LSAP_DB_HANDLE)ObjectHandle)->PhysicalNameDs,
|
|
Attributes,
|
|
AttributeCount,
|
|
Options);
|
|
|
|
if ( NT_SUCCESS( Status ) && FLAG_ON( Options, LSAPDS_REPL_CHANGE_URGENTLY ) ) {
|
|
|
|
InternalHandle->Options &= ~LSAP_DB_HANDLE_CREATED_SECRET;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbReadAttributesObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN ULONG Options,
|
|
IN OUT PLSAP_DB_ATTRIBUTE Attributes,
|
|
IN ULONG AttributeCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads the values of one or more attributes of an open LSA
|
|
Database object. A Database transaction must already be open: the write
|
|
is appended to the transaction log. The attribute names specified are
|
|
assumed to be consistent with the object type and the values supplied
|
|
are assumed to be valid. This routine will allocate memory via
|
|
MIDL_user_allocate for buffers which will receive attribute values if
|
|
requested. This memory must be freed after use by calling MIDL_User_free
|
|
after use.
|
|
|
|
WARNINGS: The Lsa Database must be in the locked state when this function
|
|
is called.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Lsa Handle of open object.
|
|
|
|
Attributes - Pointer to an array of Attribute Information blocks each
|
|
containing pointers to the attribute's Unicode Name, an optional
|
|
pointer to a buffer that will receive the value and an optional
|
|
length of the value expected in bytes.
|
|
|
|
If the AttributeValue field in this structure is specified as non-NULL,
|
|
the attribute's data will be returned in the specified buffer. In
|
|
this case, the AttributeValueLength field must specify a sufficiently
|
|
large buffer size in bytes. If the specified size is too small,
|
|
a warning is returned and the buffer size required is returned in
|
|
AttributeValueLength.
|
|
|
|
If the AttributeValue field in this structure is NULL, the routine
|
|
will allocate memory for the attribute value's buffer, via MIDL_user_allocate(). If
|
|
the AttributeValueLength field is non-zero, the number of bytes specified
|
|
will be allocated. If the size of buffer allocated is too small to
|
|
hold the attribute's value, a warning is returned. If the
|
|
AttributeValuelength field is 0, the routine will first query the size
|
|
of buffer required and then allocate its memory.
|
|
|
|
In all success cases and buffer overflow cases, the
|
|
AttributeValueLength is set upon exit to the size of data required.
|
|
|
|
AttributeCount - Count of the attributes to be read, equivalently,
|
|
this is the number of elements of the array pointed to by Attributes.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_OBJECT_NAME_NOT_FOUND - One or more of the specified
|
|
attributes do not exist. In this case, the attribute information
|
|
AttributeValue, AttributeValueLength fields are zeroised. Note
|
|
that an attempt will be made to read all of the supplied
|
|
attributes, even if one of them is not found.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAP_DB_ATTRIBUTE NextAttribute = NULL;
|
|
BOOLEAN MemoryToFree = FALSE;
|
|
ULONG MemoryToFreeCount = 0;
|
|
|
|
if ( !LsapDsIsWriteDs(ObjectHandle) ) {
|
|
|
|
for (NextAttribute = Attributes;
|
|
NextAttribute < &Attributes[AttributeCount];
|
|
NextAttribute++) {
|
|
|
|
NextAttribute->MemoryAllocated = FALSE;
|
|
|
|
// If an explicit buffer pointer is given, verify that the length
|
|
// specified is non-zero and attempt to use that buffer.
|
|
//
|
|
|
|
if (NextAttribute->AttributeValue != NULL) {
|
|
|
|
if (NextAttribute->AttributeValueLength == 0) {
|
|
|
|
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
Status = LsapDbReadAttributeObject(
|
|
ObjectHandle,
|
|
NextAttribute->AttributeName,
|
|
(PVOID) NextAttribute->AttributeValue,
|
|
(PULONG) &NextAttribute->AttributeValueLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// If the attribute was not found, set the AttributeValue
|
|
// and AttributeValueLength fields to NULL and continue.
|
|
//
|
|
|
|
if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
break;
|
|
}
|
|
|
|
NextAttribute->AttributeValue = NULL;
|
|
NextAttribute->AttributeValueLength = 0;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// No output buffer pointer has been given. If a zero buffer
|
|
// size is given, query size of memory required. Since the
|
|
// buffer length is 0, STATUS_SUCCESS should be returned rather
|
|
// than STATUS_BUFFER_OVERFLOW.
|
|
//
|
|
|
|
if (NextAttribute->AttributeValueLength == 0) {
|
|
|
|
Status = LsapDbReadAttributeObject(
|
|
ObjectHandle,
|
|
NextAttribute->AttributeName,
|
|
NULL,
|
|
(PULONG) &NextAttribute->AttributeValueLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// If the attribute was not found, set the AttributeValue
|
|
// and AttributeValueLength fields to NULL and continue.
|
|
//
|
|
|
|
if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
break;
|
|
}
|
|
|
|
NextAttribute->AttributeValue = NULL;
|
|
NextAttribute->AttributeValueLength = 0;
|
|
continue;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If the attribute value size needed is 0, return NULL pointer
|
|
//
|
|
|
|
if (NextAttribute->AttributeValueLength == 0) {
|
|
|
|
NextAttribute->AttributeValue = NULL;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Allocate memory for the buffer.
|
|
//
|
|
|
|
NextAttribute->AttributeValue =
|
|
MIDL_user_allocate(NextAttribute->AttributeValueLength);
|
|
|
|
if (NextAttribute->AttributeValue == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
NextAttribute->MemoryAllocated = TRUE;
|
|
MemoryToFree = TRUE;
|
|
MemoryToFreeCount++;
|
|
|
|
//
|
|
// Now read the attribute into the buffer.
|
|
//
|
|
|
|
Status = LsapDbReadAttributeObject(
|
|
ObjectHandle,
|
|
NextAttribute->AttributeName,
|
|
(PVOID) NextAttribute->AttributeValue,
|
|
(PULONG) &NextAttribute->AttributeValueLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto ReadAttributesError;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Use the DS method to read them all at once
|
|
//
|
|
|
|
Status = LsapDsReadAttributes( &((LSAP_DB_HANDLE)ObjectHandle)->PhysicalNameDs,
|
|
LSAPDS_OP_NO_LOCK,
|
|
Attributes,
|
|
AttributeCount );
|
|
}
|
|
|
|
ReadAttributesFinish:
|
|
|
|
return(Status);
|
|
|
|
ReadAttributesError:
|
|
|
|
//
|
|
// If memory was allocated for any values read, it must be freed.
|
|
//
|
|
|
|
if (MemoryToFree) {
|
|
|
|
for (NextAttribute = &Attributes[0];
|
|
(MemoryToFreeCount > 0) &&
|
|
(NextAttribute < &Attributes[AttributeCount]);
|
|
NextAttribute++) {
|
|
|
|
if (NextAttribute->MemoryAllocated) {
|
|
|
|
MIDL_user_free( NextAttribute->AttributeValue );
|
|
NextAttribute->AttributeValue = NULL;
|
|
MemoryToFreeCount--;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto ReadAttributesFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbDeleteAttributeObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN PUNICODE_STRING AttributeNameU,
|
|
IN BOOLEAN DeleteSecurely
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes an attribute of an open LSA Database object.
|
|
A Database transaction must already be open: the delete actions are
|
|
appended to the transaction log.
|
|
|
|
WARNING: The Lsa Database must be in the locked state when this function
|
|
is called.
|
|
|
|
The LSA Database is implemented as a subtree of the Configuration
|
|
Registry. In this implementation, Lsa Database objects correspond
|
|
to Registry keys and "attributes" and their "values" correspond to
|
|
Registry "subkeys" and "values" of the Registry key representing the
|
|
object.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Lsa Handle of open object.
|
|
|
|
AttributeNameU - Pointer to Unicode string containing the name of the
|
|
attribute whose value is to be written.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING PhysicalSubKeyNameU;
|
|
ULONG AttributeLength = 0;
|
|
|
|
//
|
|
// Verify that the LSA Database is locked
|
|
// (The lock for the specified object type must be locked.)
|
|
//
|
|
|
|
// ASSERT (LsapDbIsLocked());
|
|
|
|
//
|
|
// The Registry code will actually create a key if one does not exist, so
|
|
// probe for the existence of the key first.
|
|
//
|
|
|
|
Status = LsapDbReadAttributeObject(
|
|
ObjectHandle,
|
|
AttributeNameU,
|
|
NULL,
|
|
&AttributeLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteAttributeObjectError;
|
|
}
|
|
|
|
//
|
|
// We need to construct the Physical Name the sub key relative
|
|
// to the LSA Database root node in the Registry. This is done by
|
|
// concatenating the Physical Object Name stored in the object handle with
|
|
// a "\" and the given sub key name.
|
|
//
|
|
|
|
Status = LsapDbLogicalToPhysicalSubKey(
|
|
ObjectHandle,
|
|
&PhysicalSubKeyNameU,
|
|
AttributeNameU
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteAttributeObjectError;
|
|
}
|
|
|
|
//
|
|
// Now log the sub key write as a Registry Transaction
|
|
//
|
|
|
|
Status = LsapRegDeleteAttribute(
|
|
&PhysicalSubKeyNameU,
|
|
DeleteSecurely,
|
|
AttributeLength
|
|
);
|
|
|
|
RtlFreeUnicodeString(&PhysicalSubKeyNameU);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteAttributeObjectError;
|
|
}
|
|
|
|
DeleteAttributeObjectFinish:
|
|
|
|
return(Status);
|
|
|
|
DeleteAttributeObjectError:
|
|
|
|
//
|
|
// Add any cleanup required on error paths only here.
|
|
//
|
|
|
|
goto DeleteAttributeObjectFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbDeleteAttributesObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN PLSAP_DB_ATTRIBUTE Attributes,
|
|
IN ULONG AttributeCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes the values of one or more attributes of an open LSA
|
|
Database object. A Database transaction must already be open: the write
|
|
is appended to the transaction log. The attribute names specified are
|
|
assumed to be consistent with the object type and the values supplied
|
|
are assumed to be valid.
|
|
|
|
WARNINGS: The Lsa Database must be in the locked state when this function
|
|
is called.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Lsa Handle of open object.
|
|
|
|
Attributes - Pointer to an array of Attribute Information blocks each
|
|
containing pointers to the attribute's Unicode Name, the value
|
|
to be stored, and the length of the value in bytes.
|
|
|
|
AttributeCount - Count of the attributes to be written, equivalently,
|
|
this is the number of elements of the array pointed to by Attributes.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Index;
|
|
|
|
if ( !LsapDsIsWriteDs( ObjectHandle ) ) {
|
|
|
|
for(Index = 0; Index < AttributeCount; Index++) {
|
|
|
|
Status = LsapDbDeleteAttributeObject(
|
|
ObjectHandle,
|
|
Attributes[Index].AttributeName,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = LsapDsDeleteAttributes( &((LSAP_DB_HANDLE)ObjectHandle)->PhysicalNameDs,
|
|
Attributes,
|
|
AttributeCount);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
typedef NTSTATUS ( *LsapDsPolicyNotify )(
|
|
IN SECURITY_DB_TYPE DbType,
|
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|
IN PSID ObjectSid
|
|
);
|
|
|
|
NTSTATUS
|
|
LsapSceNotify(
|
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|
IN PSID ObjectSid OPTIONAL
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS SavedStatus = STATUS_SUCCESS;
|
|
static LsapDsPolicyNotify SceNotify = NULL;
|
|
static HANDLE DllHandle = NULL;
|
|
static LARGE_INTEGER liZero = {0};
|
|
|
|
ASSERT( !g_ScePolicyLocked );
|
|
|
|
if ( !LsapDbState.ReplicatorNotificationEnabled ) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( SceNotify == NULL ) {
|
|
|
|
if ( DllHandle != NULL ) {
|
|
|
|
FreeLibrary( DllHandle );
|
|
DllHandle = NULL;
|
|
}
|
|
|
|
DllHandle = LoadLibraryA( "scecli" );
|
|
|
|
if ( DllHandle == NULL ) {
|
|
|
|
LsapDsDebugOut(( DEB_ERROR, "Failed to load SCECLI.DLL\n" ));
|
|
if ( NT_SUCCESS(SavedStatus)) {
|
|
SavedStatus = STATUS_DLL_NOT_FOUND;
|
|
}
|
|
} else {
|
|
SceNotify = ( LsapDsPolicyNotify )GetProcAddress( DllHandle,
|
|
"SceNotifyPolicyDelta" );
|
|
|
|
if ( SceNotify == NULL ) {
|
|
|
|
LsapDsDebugOut(( DEB_ERROR,
|
|
"Failed to find SceNotifyPolicyDelta in SCECLI.DLL\n" ));
|
|
if ( NT_SUCCESS(SavedStatus)) {
|
|
SavedStatus = STATUS_ENTRYPOINT_NOT_FOUND;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// The only two types of objects SCE cares about are Policy and Account objects
|
|
//
|
|
|
|
switch ( ObjectType ) {
|
|
|
|
case SecurityDbObjectLsaPolicy:
|
|
case SecurityDbObjectLsaAccount:
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Shouldn't call this routine with other object types
|
|
//
|
|
|
|
ASSERT( FALSE );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( SceNotify != NULL ) {
|
|
|
|
Status = ( *SceNotify )(
|
|
SecurityDbLsa,
|
|
DeltaType,
|
|
ObjectType,
|
|
ObjectSid );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
if ( NT_SUCCESS(SavedStatus)) {
|
|
SavedStatus = Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SavedStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
LsapNetNotifyDelta (
|
|
IN SECURITY_DB_TYPE DbType,
|
|
IN LARGE_INTEGER SerialNumber,
|
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|
IN ULONG ObjectRid,
|
|
IN PSID ObjectSid,
|
|
IN PUNICODE_STRING ObjectName,
|
|
IN DWORD ReplicateImmediately,
|
|
IN PSAM_DELTA_DATA MemberId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a wrapper function for Netlogon's I_NetNotifyDelta
|
|
|
|
Arguments:
|
|
|
|
Same as I_NetNotifyDelta
|
|
|
|
Return Value:
|
|
|
|
Same as I_NetNotifyDelta
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Notify the LSA Database Replicator of the change.
|
|
//
|
|
|
|
return ( I_NetNotifyDelta(
|
|
DbType,
|
|
SerialNumber,
|
|
DeltaType,
|
|
ObjectType,
|
|
ObjectRid,
|
|
ObjectSid,
|
|
ObjectName,
|
|
ReplicateImmediately,
|
|
MemberId ));
|
|
}
|
|
|
|
NTSTATUS
|
|
LsapDbNotifyChangeObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function notifies the LSA Database Replicator (if any) of a
|
|
change to an object. Change notifications for Secret objects specify
|
|
that replication of the change should occur immediately.
|
|
|
|
WARNING! All parameters passed to this routine are assumed valid.
|
|
No checking will be done.
|
|
|
|
Must be entered with the RegistryLock locked.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Handle to an LSA object. This is expected to have
|
|
already been validated.
|
|
|
|
SecurityDbDeltaType - Specifies the type of change being made. The
|
|
following values only are relevant:
|
|
|
|
SecurityDbNew - Indicates that a new object has been created.
|
|
SecurityDbDelete - Indicates that an object is being deleted.
|
|
SecurityDbChange - Indicates that the attributes of an object
|
|
are being changed, including creation or deletion of
|
|
attributes.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_INVALID_HANDLE - The specified handle is invalid. This
|
|
error is only returned if the Object Type Id in the handle
|
|
is invalid.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
SECURITY_DB_OBJECT_TYPE ObjectType = SecurityDbObjectLsaTDomain;
|
|
UNICODE_STRING ObjectName;
|
|
PSID ObjectSid = NULL;
|
|
ULONG ObjectRid = 0;
|
|
UCHAR SubAuthorityCount;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
|
|
BOOLEAN ReplicateImmediately = FALSE;
|
|
|
|
ASSERT( LsapDbIsLocked( &LsapDbState.RegistryLock ));
|
|
|
|
ObjectName.Buffer = NULL;
|
|
ObjectName.Length = ObjectName.MaximumLength = 0;
|
|
|
|
ASSERT(LsapDbState.ReplicatorNotificationEnabled);
|
|
|
|
//
|
|
// The problem is that many code paths in the lsa pass a 0 for SecurityDbDeltaType.
|
|
// without specifying omit replicator notification. This is incorrect and causes
|
|
// a notification with a 0 specified for the delta type. This is causes a full
|
|
// sync of all NT4 BDC's. To safegaurd against this case
|
|
|
|
ASSERT(SecurityDbDeltaType!=0);
|
|
if (0==SecurityDbDeltaType)
|
|
{
|
|
SecurityDbDeltaType = SecurityDbChange;
|
|
}
|
|
|
|
//
|
|
// Convert the Lsa Database Object Type to a Database Delta Type.
|
|
//
|
|
|
|
switch (InternalHandle->ObjectTypeId) {
|
|
|
|
case PolicyObject:
|
|
|
|
ObjectType = SecurityDbObjectLsaPolicy;
|
|
break;
|
|
|
|
case AccountObject:
|
|
|
|
ObjectType = SecurityDbObjectLsaAccount;
|
|
break;
|
|
|
|
case TrustedDomainObject:
|
|
|
|
ObjectType = SecurityDbObjectLsaTDomain;
|
|
break;
|
|
|
|
case SecretObject:
|
|
|
|
ObjectType = SecurityDbObjectLsaSecret;
|
|
ReplicateImmediately = TRUE;
|
|
break;
|
|
|
|
default:
|
|
|
|
Status = STATUS_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the Name or Sid of the object from its handle. If the object
|
|
// is of a type such as SecretObject that is accessed by Name, then
|
|
// the object's externally known name is equal to its internal
|
|
// Logical Name contained in the handle.
|
|
//
|
|
|
|
if ( LsapDbAccessedBySidObject( InternalHandle->ObjectTypeId )) {
|
|
|
|
ObjectSid = InternalHandle->Sid;
|
|
|
|
if ( InternalHandle->ObjectTypeId != TrustedDomainObject ) {
|
|
|
|
ASSERT( ObjectSid );
|
|
|
|
if ( ObjectSid == NULL ) {
|
|
|
|
Status = STATUS_INVALID_SID;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
SubAuthorityCount = *RtlSubAuthorityCountSid( ObjectSid );
|
|
ObjectRid = *RtlSubAuthoritySid( ObjectSid, SubAuthorityCount -1 );
|
|
}
|
|
|
|
} else if (LsapDsIsWriteDs( InternalHandle) ||
|
|
LsapDbAccessedByNameObject( InternalHandle->ObjectTypeId )) {
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
NULL,
|
|
&ObjectName,
|
|
&InternalHandle->LogicalNameU
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Currently, an object is either accessed by Sid or by Name, so
|
|
// something is wrong if both of the above chacks have failed.
|
|
//
|
|
|
|
Status = STATUS_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Notify the LSA Database Replicator of the change.
|
|
//
|
|
|
|
Status = LsapNetNotifyDelta (
|
|
SecurityDbLsa,
|
|
LsapDbState.PolicyModificationInfo.ModifiedId,
|
|
SecurityDbDeltaType,
|
|
ObjectType,
|
|
ObjectRid,
|
|
ObjectSid,
|
|
&ObjectName,
|
|
ReplicateImmediately,
|
|
NULL
|
|
);
|
|
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// If we allocated memory for the Object Name Unicode buffer, free it.
|
|
//
|
|
|
|
if (ObjectName.Buffer != NULL) {
|
|
|
|
MIDL_user_free( ObjectName.Buffer );
|
|
}
|
|
|
|
//
|
|
// Suppress any error and return STATUS_SUCCESS. Currently, there is
|
|
// no meaningful action an LSA client of this routine can take.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbVerifyInformationObject(
|
|
IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function verifies that the information specified in passed
|
|
ObjectInformation is syntactically valid.
|
|
|
|
Arguments:
|
|
|
|
ObjectInformation - Pointer to information describing this object. The
|
|
following information items must be specified:
|
|
|
|
o Object Type Id
|
|
o Object Logical Name (as ObjectAttributes->ObjectName, a pointer to
|
|
a Unicode string)
|
|
o Container object handle (for any object except the root Policy object).
|
|
|
|
All other fields in ObjectAttributes portion of ObjectInformation
|
|
such as SecurityDescriptor are ignored.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_INVALID_PARAMETER - Invalid object information given
|
|
- ObjectInformation is NULL
|
|
- Object Type Id is out of range
|
|
- No Logical Name pointer given
|
|
- Logical Name not a pointer to a Unicode String (TBS)
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = ObjectInformation->ObjectTypeId;
|
|
|
|
//
|
|
// Verify that ObjectInformation is given
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(ObjectInformation)) {
|
|
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Validate the Object Type Id. It must be in range.
|
|
//
|
|
|
|
if (!LsapDbIsValidTypeObject(ObjectTypeId)) {
|
|
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Verify that a Logical Name is given. A pointer to a Unicode string
|
|
// is expected.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(ObjectInformation->ObjectAttributes.ObjectName)) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbSidToLogicalNameObject(
|
|
IN PSID Sid,
|
|
OUT PUNICODE_STRING LogicalNameU
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function generates the Logical Name (Internal LSA Database Name)
|
|
of an object from its Sid. Currently, only the Relative Id (lowest
|
|
sub-authority) is used due to Registry and hence Lsa Database limits
|
|
on name components to 8 characters. The Rid is extracted and converted
|
|
to an 8-digit Unicode Integer.
|
|
|
|
Arguments:
|
|
|
|
Sid - Pointer to the Sid to be looked up. It is the caller's
|
|
responsibility to ensure that the Sid has valid syntax.
|
|
|
|
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;
|
|
|
|
//
|
|
// First, verify that the given Sid is valid
|
|
//
|
|
|
|
if (!RtlValidSid( Sid )) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
Status = RtlConvertSidToUnicodeString( LogicalNameU, Sid, TRUE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbGetNamesObject(
|
|
IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
|
|
IN ULONG CreateHandleOptions,
|
|
OUT OPTIONAL PUNICODE_STRING LogicalNameU,
|
|
OUT OPTIONAL PUNICODE_STRING PhysicalNameU,
|
|
OUT OPTIONAL PUNICODE_STRING PhysicalNameDs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the Logical and/or Physical Names of an object
|
|
given an object information buffer. Memory will be allocated for
|
|
the Unicode String Buffers that will receive the name(s).
|
|
|
|
The Logical Name of an object is the path of the object within the
|
|
LSA Database relative to its Classifying Directory. The Logical Name
|
|
of an object is implemntation-dependent and on current implementations
|
|
is equal to one of the following depending on object type:
|
|
|
|
o The External Name of the object (if any)
|
|
o The Relative Id (lowest sub-authority) in the object's Sid (if any)
|
|
converted to an 8-digit integer, including leading 0's added as
|
|
padding.
|
|
|
|
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().
|
|
|
|
Example of Physical Name computation:
|
|
|
|
Consider the user or group account object ScottBi
|
|
|
|
Container Object Logical Name: Policy
|
|
Container Object Physical Name: Policy (no Classifying Directory or
|
|
Container Object exists)
|
|
Classifying Directory for ScottBi: Accounts
|
|
Logical Name of Object: ScottBi
|
|
Physical Name of Object Policy\Accounts\ScottBi
|
|
|
|
Note that the Physical Name is exactly the Registry path relative to
|
|
the Security directory.
|
|
|
|
WARNING: The Lsa Database must be in the locked state when this function
|
|
is called.
|
|
|
|
Arguments:
|
|
|
|
ObjectInformation - Pointer to object information containing as a minimum
|
|
the object's Logical Name, Container Object's handle and object type
|
|
id.
|
|
|
|
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. This is only initialized
|
|
the object can reside in the registry
|
|
|
|
PhysicalNameDs - Optional pointer to Unicode String structure which will
|
|
receive the Physical Name of the object if it resides in the DS. Same
|
|
caveats apply.
|
|
|
|
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.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PUNICODE_STRING ContainerPhysicalNameU = NULL;
|
|
PUNICODE_STRING ClassifyingDirU = NULL;
|
|
UNICODE_STRING IntermediatePath1U;
|
|
PUNICODE_STRING JoinedPath1U = &IntermediatePath1U;
|
|
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = ObjectInformation->ObjectTypeId;
|
|
POBJECT_ATTRIBUTES ObjectAttributes = &ObjectInformation->ObjectAttributes;
|
|
UNICODE_STRING TempLogicalNameU, TempDomainLogicalName;
|
|
UUID Uuid;
|
|
PWSTR GuidString;
|
|
RPC_STATUS RpcStatus;
|
|
ULONG GuidLength, BufferLength, SkipLength;
|
|
PBYTE ScratchBuffer = NULL, CopyStart;
|
|
BOOLEAN ObjectShouldExist = TRUE;
|
|
|
|
//
|
|
// Initialize
|
|
//
|
|
RtlInitUnicodeString( &IntermediatePath1U, NULL );
|
|
RtlInitUnicodeString( &TempLogicalNameU, NULL );
|
|
RtlInitUnicodeString( &TempDomainLogicalName, NULL );
|
|
|
|
//
|
|
// Verify that the LSA Database is locked
|
|
// (The lock for the specified object type must be locked.)
|
|
//
|
|
// ASSERT (LsapDbIsLocked());
|
|
|
|
//
|
|
// Capture the Logical Name of the object into permanent memory.
|
|
//
|
|
LSAPDS_ALLOC_AND_COPY_UNICODE_STRING_ON_SUCCESS( Status,
|
|
&TempLogicalNameU,
|
|
ObjectInformation->ObjectAttributes.ObjectName );
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If the Logical Name of the object is requested, return this.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LogicalNameU)) {
|
|
|
|
*LogicalNameU = TempLogicalNameU;
|
|
}
|
|
|
|
//
|
|
// If the Physical Name of the object is not required, just return.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(PhysicalNameU)) {
|
|
|
|
PhysicalNameU->Buffer = NULL;
|
|
Status = LsapRegGetPhysicalObjectName(
|
|
ObjectInformation,
|
|
&TempLogicalNameU,
|
|
PhysicalNameU );
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) && ARGUMENT_PRESENT( PhysicalNameDs) ) {
|
|
|
|
PhysicalNameDs->Buffer = NULL;
|
|
|
|
//
|
|
// Copy the logical name, in case we have to return the current copy...
|
|
//
|
|
LSAPDS_ALLOC_AND_COPY_UNICODE_STRING_ON_SUCCESS( Status,
|
|
&TempDomainLogicalName,
|
|
&TempLogicalNameU );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( FLAG_ON( CreateHandleOptions, LSAP_DB_CREATE_HANDLE_MORPH ) ) {
|
|
|
|
ObjectShouldExist = FALSE;
|
|
}
|
|
|
|
//
|
|
// If the name is too long, truncate it...
|
|
//
|
|
if ( FLAG_ON( CreateHandleOptions, LSAP_DB_CREATE_HANDLE_MORPH ) ) {
|
|
|
|
LsapTruncateUnicodeString( &TempDomainLogicalName, MAX_RDN_KEY_SIZE );
|
|
}
|
|
|
|
Status = LsapDsGetPhysicalObjectName( ObjectInformation,
|
|
ObjectShouldExist,
|
|
&TempDomainLogicalName,
|
|
PhysicalNameDs );
|
|
|
|
if ( (Status == STATUS_OBJECT_NAME_COLLISION) &&
|
|
(ObjectInformation->ObjectTypeId == NewTrustedDomainObject) &&
|
|
FLAG_ON( CreateHandleOptions, LSAP_DB_CREATE_HANDLE_MORPH ) ) {
|
|
|
|
//
|
|
// Got a name collion. Morph the name by replacing the last digits with a GUID
|
|
//
|
|
RpcStatus = UuidCreate ( &Uuid );
|
|
|
|
if ( RpcStatus == RPC_S_OK || RpcStatus == RPC_S_UUID_LOCAL_ONLY ) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
RpcStatus = UuidToStringW( &Uuid, &GuidString );
|
|
|
|
if ( RpcStatus == RPC_S_OK ) {
|
|
|
|
GuidLength = wcslen( GuidString );
|
|
|
|
//
|
|
// Now, we have to handle the cases of the various string lengths...
|
|
//
|
|
BufferLength = 0;
|
|
if ( ( TempDomainLogicalName.Length / sizeof( WCHAR ) ) + GuidLength >=
|
|
MAX_RDN_KEY_SIZE ) {
|
|
|
|
LsapTruncateUnicodeString( &TempDomainLogicalName, MAX_RDN_KEY_SIZE );
|
|
BufferLength = MAX_RDN_KEY_SIZE;
|
|
SkipLength = MAX_RDN_KEY_SIZE - GuidLength;
|
|
|
|
} else {
|
|
|
|
BufferLength = ( TempDomainLogicalName.Length / sizeof( WCHAR ) ) + GuidLength;
|
|
SkipLength = TempDomainLogicalName.Length / sizeof( WCHAR );
|
|
|
|
}
|
|
|
|
if ( BufferLength >
|
|
( TempDomainLogicalName.MaximumLength / sizeof( WCHAR ) ) ) {
|
|
|
|
//
|
|
// Have to allocate a new buffer
|
|
//
|
|
ScratchBuffer =
|
|
LsapAllocateLsaHeap( ( BufferLength + 1 ) * sizeof( WCHAR ) );
|
|
|
|
if ( ScratchBuffer == NULL ) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
RtlCopyMemory( ScratchBuffer,
|
|
TempDomainLogicalName.Buffer,
|
|
TempDomainLogicalName.Length );
|
|
|
|
LsapFreeLsaHeap( TempDomainLogicalName.Buffer );
|
|
TempDomainLogicalName.Length = ( USHORT )BufferLength * sizeof( WCHAR );
|
|
TempDomainLogicalName.MaximumLength = TempDomainLogicalName.Length + sizeof( WCHAR );
|
|
TempDomainLogicalName.Buffer = ( PWSTR )ScratchBuffer;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, build the new string
|
|
//
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
RtlCopyMemory( ( ( PWSTR )TempDomainLogicalName.Buffer + SkipLength ),
|
|
GuidString,
|
|
GuidLength );
|
|
}
|
|
|
|
RpcStringFreeW( &GuidString );
|
|
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) && RpcStatus != RPC_S_OK ) {
|
|
|
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Try to build the name again. If this fails, such is life...
|
|
//
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
Status = LsapDsGetPhysicalObjectName( ObjectInformation,
|
|
ObjectShouldExist,
|
|
&TempDomainLogicalName,
|
|
PhysicalNameDs );
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
// Do cleanup that does not depend on success or failure of this function
|
|
//
|
|
LsapFreeLsaHeap( TempDomainLogicalName.Buffer );
|
|
|
|
|
|
// Do cleanup required on failure of this function
|
|
//
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
LsapFreeLsaHeap( TempLogicalNameU.Buffer );
|
|
|
|
if ( ARGUMENT_PRESENT(LogicalNameU) ) {
|
|
// we dont need to free this since this simply points to TempLogicalNameU
|
|
// which is already freed.
|
|
LogicalNameU->Buffer = NULL;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(PhysicalNameU) ) {
|
|
|
|
LsapFreeLsaHeap( PhysicalNameU->Buffer );
|
|
PhysicalNameU->Buffer = NULL;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(PhysicalNameDs) ) {
|
|
|
|
LsapFreeLsaHeap( PhysicalNameDs->Buffer );
|
|
PhysicalNameDs->Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
LsapDbIsLocked(
|
|
IN PSAFE_CRITICAL_SECTION CritSect
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if LSA Database is locked.
|
|
|
|
Arguments:
|
|
|
|
CritSect to check.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if LSA database is locked, else false.
|
|
|
|
--*/
|
|
|
|
{
|
|
return (BOOLEAN)( SafeCritsecLockCount( CritSect ) != -1L);
|
|
}
|
|
|
|
BOOLEAN
|
|
LsapDbResourceIsLocked(
|
|
IN PSAFE_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if LSA Database is locked.
|
|
|
|
Arguments:
|
|
|
|
CritSect to check.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if LSA database is locked, else false.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN IsLocked;
|
|
|
|
SafeEnterResourceCritsec( Resource );
|
|
IsLocked = ( SafeNumberOfActive( Resource ) != 0);
|
|
SafeLeaveResourceCritsec( Resource );
|
|
|
|
return IsLocked;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsarQuerySecurityObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
OUT PLSAPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaQuerySecurityObject API returns security information assigned
|
|
to an LSA Database object.
|
|
|
|
Based on the caller's access rights and privileges, this procedure will
|
|
return a security descriptor containing any or all of the object's owner
|
|
ID, group ID, discretionary ACL or system ACL. To read the owner ID,
|
|
group ID, or the discretionary ACL, the caller must be granted
|
|
READ_CONTROL access to the object. To read the system ACL, the caller must
|
|
have SeSecurityPrivilege privilege.
|
|
|
|
This API is modelled after the NtQuerySecurityObject() system service.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - A handle to an existing object in the LSA Database.
|
|
|
|
SecurityInformation - Supplies a value describing which pieces of
|
|
security information are being queried. The values that may be
|
|
specified are the same as those defined in the NtSetSecurityObject()
|
|
API section.
|
|
|
|
SecurityDescriptor - Receives a pointer to a buffer containing the
|
|
requested security information. This information is returned in
|
|
the form of a Self-Relative Security Descriptor.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INVALID_PARAMETER - An invalid parameter has been specified.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status,
|
|
IgnoreStatus;
|
|
|
|
LSAP_DB_HANDLE
|
|
InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
|
|
|
|
ACCESS_MASK
|
|
RequiredAccess = 0;
|
|
|
|
BOOLEAN
|
|
Present,
|
|
IgnoreBoolean;
|
|
|
|
LSAP_DB_ATTRIBUTE
|
|
Attribute;
|
|
|
|
PLSAPR_SR_SECURITY_DESCRIPTOR
|
|
RpcSD = NULL;
|
|
|
|
SECURITY_DESCRIPTOR_RELATIVE
|
|
*SD = NULL;
|
|
|
|
SECURITY_DESCRIPTOR
|
|
*ReturnSD = NULL;
|
|
|
|
ULONG
|
|
ReturnSDLength;
|
|
BOOLEAN ObjectReferenced = FALSE;
|
|
|
|
LsarpReturnCheckSetup();
|
|
|
|
|
|
if (!ARGUMENT_PRESENT( SecurityDescriptor )) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// If this is a non-Trusted client, determine the required accesses
|
|
// for querying the object's Security Descriptor. These accesses
|
|
// depend on the information being queried.
|
|
//
|
|
|
|
LsapRtlQuerySecurityAccessMask( SecurityInformation, &RequiredAccess );
|
|
|
|
|
|
//
|
|
// Acquire the Lsa Database lock. Verify that the object handle
|
|
// is a valid handle (of any type) and is trusted or has
|
|
// all of the required accesses granted. Reference the container
|
|
// object handle.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
ObjectHandle,
|
|
RequiredAccess,
|
|
InternalHandle->ObjectTypeId,
|
|
InternalHandle->ObjectTypeId,
|
|
LSAP_DB_LOCK
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
ObjectReferenced = TRUE;
|
|
|
|
//
|
|
// Handle objects in the DS
|
|
//
|
|
|
|
if ( LsapDsIsHandleDsHandle( InternalHandle )) {
|
|
|
|
//
|
|
// A trusted client needs an SD to replicate to NT 4 BDCs.
|
|
// Give it the default SD for the object.
|
|
//
|
|
|
|
if ( InternalHandle->Trusted ) {
|
|
|
|
Status = LsapDbCreateSDObject(
|
|
InternalHandle->ContainerHandle,
|
|
ObjectHandle,
|
|
(PSECURITY_DESCRIPTOR*) &SD );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Don't support this API for objects in the DS
|
|
// (Otherwise, we'd have to translate the SD to DS format.)
|
|
//
|
|
} else {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// For objects in the registry,
|
|
// read the security descriptor from the registry.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Read the existing Security Descriptor for the object. This always
|
|
// exists as the value of the SecDesc attribute of the object.
|
|
//
|
|
|
|
LsapDbInitializeAttributeDs(
|
|
&Attribute,
|
|
SecDesc,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
Status = LsapDbReadAttribute( ObjectHandle, &Attribute );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
SD = Attribute.AttributeValue;
|
|
}
|
|
|
|
|
|
//
|
|
// ASSERT: SD is the complete security descriptor of the object.
|
|
//
|
|
// Elimate components that weren't requested.
|
|
//
|
|
ASSERT( SD != NULL );
|
|
|
|
if ( !(SecurityInformation & OWNER_SECURITY_INFORMATION)) {
|
|
SD->Owner = 0;
|
|
}
|
|
|
|
if ( !(SecurityInformation & GROUP_SECURITY_INFORMATION)) {
|
|
SD->Group = 0;
|
|
}
|
|
|
|
if ( !(SecurityInformation & DACL_SECURITY_INFORMATION)) {
|
|
SD->Control &= (~SE_DACL_PRESENT);
|
|
}
|
|
|
|
if ( !(SecurityInformation & SACL_SECURITY_INFORMATION)) {
|
|
SD->Control &= (~SE_SACL_PRESENT);
|
|
}
|
|
|
|
|
|
//
|
|
// Now copy the parts of the security descriptor that we are going to return.
|
|
//
|
|
|
|
ReturnSDLength = 0;
|
|
ReturnSD = NULL;
|
|
Status = RtlMakeSelfRelativeSD( (PSECURITY_DESCRIPTOR) SD,
|
|
ReturnSD,
|
|
&ReturnSDLength );
|
|
|
|
if (Status == STATUS_BUFFER_TOO_SMALL) { // This is the expected case
|
|
|
|
ReturnSD = MIDL_user_allocate( ReturnSDLength );
|
|
|
|
if (ReturnSD == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlMakeSelfRelativeSD( (PSECURITY_DESCRIPTOR) SD,
|
|
(PSECURITY_DESCRIPTOR) ReturnSD,
|
|
&ReturnSDLength );
|
|
|
|
if ( !NT_SUCCESS(Status)) {
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
goto Cleanup;
|
|
}
|
|
} else {
|
|
if ( NT_SUCCESS(Status) ) {
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate the first block of returned memory.
|
|
//
|
|
|
|
RpcSD = MIDL_user_allocate( sizeof(LSAPR_SR_SECURITY_DESCRIPTOR) );
|
|
|
|
if (RpcSD == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RpcSD->Length = ReturnSDLength;
|
|
RpcSD->SecurityDescriptor = (PUCHAR)( (PVOID)ReturnSD );
|
|
ReturnSD = NULL;
|
|
|
|
Cleanup:
|
|
|
|
|
|
//
|
|
// free the attribute read from disk
|
|
//
|
|
if ( SD != NULL ) {
|
|
MIDL_user_free( SD );
|
|
}
|
|
|
|
if ( ReturnSD != NULL ) {
|
|
MIDL_user_free( ReturnSD );
|
|
}
|
|
|
|
if ( ObjectReferenced ) {
|
|
|
|
IgnoreStatus = LsapDbDereferenceObject(
|
|
&ObjectHandle,
|
|
InternalHandle->ObjectTypeId,
|
|
InternalHandle->ObjectTypeId,
|
|
LSAP_DB_LOCK,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
|
|
}
|
|
|
|
*SecurityDescriptor = RpcSD;
|
|
|
|
LsarpReturnPrologue();
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsarSetSecurityObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
IN PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaSetSecurityObject API takes a well formaed Security Descriptor
|
|
and assigns specified portions of it to an object. Based on the flags set
|
|
in the SecurityInformation parameter and the caller's access rights, this
|
|
procedure will replace any or alll of the security information associated
|
|
with the object.
|
|
|
|
The caller must have WRITE_OWNER access to the object to change the
|
|
owner or Primary group of the object. The caller must have WRITE_DAC
|
|
access to the object to change the Discretionary ACL. The caller must
|
|
have SeSecurityPrivilege to assign a system ACL to an object.
|
|
|
|
This API is modelled after the NtSetSecurityObject() system service.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - A handle to an existing object in the LSA Database.
|
|
|
|
SecurityInformation - Indicates which security information is to be
|
|
applied to the object. The values that may be specified are the
|
|
same as those defined in the NtSetSecurityObject() API section.
|
|
The value(s) to be assigned are passed in the SecurityDescriptor
|
|
parameter.
|
|
|
|
SecurityDescriptor - A pointer to a well formed Self-Relative
|
|
Security Descriptor.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INVALID_PARAMETER - An invalid parameter has been specified.
|
|
|
|
STATUS_INVALID_SECURITY_DESCR - A bad security descriptor was given
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS SecondaryStatus = STATUS_SUCCESS;
|
|
ACCESS_MASK RequiredAccess = 0;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
|
|
LSAP_DB_ATTRIBUTE Attribute;
|
|
PSECURITY_DESCRIPTOR SetSD = NULL;
|
|
PSECURITY_DESCRIPTOR RetrieveSD = NULL;
|
|
PSECURITY_DESCRIPTOR ModificationSD = NULL;
|
|
ULONG RetrieveSDLength;
|
|
ULONG SetSDLength;
|
|
BOOLEAN ObjectReferenced = FALSE;
|
|
HANDLE ClientToken = NULL;
|
|
|
|
LsarpReturnCheckSetup();
|
|
|
|
//
|
|
// Verify that a Security Descriptor has been passed.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT( SecurityDescriptor )) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT( SecurityDescriptor->SecurityDescriptor )) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
//
|
|
// Verify its a valid security descriptor
|
|
//
|
|
|
|
if ( !RtlValidRelativeSecurityDescriptor(
|
|
(PSECURITY_DESCRIPTOR)SecurityDescriptor->SecurityDescriptor,
|
|
SecurityDescriptor->Length,
|
|
SecurityInformation )) {
|
|
|
|
Status = STATUS_INVALID_SECURITY_DESCR;
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
ModificationSD = (PSECURITY_DESCRIPTOR)(SecurityDescriptor->SecurityDescriptor);
|
|
|
|
//
|
|
// If the caller is non-trusted, figure the accesses required
|
|
// to update the object's Security Descriptor based on the
|
|
// information being changed.
|
|
//
|
|
|
|
if (!InternalHandle->Trusted) {
|
|
|
|
LsapRtlSetSecurityAccessMask( SecurityInformation, &RequiredAccess);
|
|
}
|
|
|
|
//
|
|
// Acquire the Lsa Database lock. Verify that the object handle
|
|
// is a valid handle (of any type), and is trusted or has
|
|
// all of the desired accesses granted. Reference the container
|
|
// object handle.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
ObjectHandle,
|
|
RequiredAccess,
|
|
InternalHandle->ObjectTypeId,
|
|
InternalHandle->ObjectTypeId,
|
|
LSAP_DB_LOCK | LSAP_DB_START_TRANSACTION
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
ObjectReferenced = TRUE;
|
|
|
|
//
|
|
// Don't support this API for objects in the DS
|
|
// (Otherwise, we'd have to translate the SD to DS format.)
|
|
//
|
|
|
|
if ( LsapDsIsHandleDsHandle( InternalHandle )) {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
//
|
|
// Read the existing Security Descriptor for the object. This always
|
|
// exists as the value of the SecDesc attribute of the object.
|
|
//
|
|
|
|
LsapDbInitializeAttributeDs(
|
|
&Attribute,
|
|
SecDesc,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
Status = LsapDbReadAttribute( ObjectHandle, &Attribute );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
//
|
|
// Copy the retrieved descriptor into process heap so we can use
|
|
// RTL routines.
|
|
//
|
|
|
|
RetrieveSD = Attribute.AttributeValue;
|
|
RetrieveSDLength = Attribute.AttributeValueLength;
|
|
|
|
|
|
if (RetrieveSD == NULL) {
|
|
Status = STATUS_INTERNAL_DB_CORRUPTION;
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
if (RetrieveSDLength == 0) {
|
|
Status = STATUS_INTERNAL_DB_CORRUPTION;
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
SetSD = RtlAllocateHeap( RtlProcessHeap(), 0, RetrieveSDLength );
|
|
|
|
if (SetSD == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
RtlCopyMemory( SetSD, RetrieveSD, RetrieveSDLength );
|
|
|
|
//
|
|
// If the caller is replacing the owner, then a handle to the impersonation
|
|
// token is necessary.
|
|
//
|
|
|
|
ClientToken = 0;
|
|
|
|
if (SecurityInformation & OWNER_SECURITY_INFORMATION) {
|
|
|
|
if (!InternalHandle->Trusted) {
|
|
|
|
//
|
|
// Client is non-trusted. Impersonate the client and
|
|
// obtain a handle to the impersonation token.
|
|
//
|
|
|
|
Status = I_RpcMapWin32Status(RpcImpersonateClient( NULL ));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
Status = NtOpenThreadToken(
|
|
NtCurrentThread(),
|
|
TOKEN_QUERY,
|
|
TRUE, //OpenAsSelf
|
|
&ClientToken
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (Status != STATUS_NO_TOKEN) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Stop impersonating the client
|
|
//
|
|
|
|
SecondaryStatus = I_RpcMapWin32Status(RpcRevertToSelf());
|
|
|
|
if (!NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Client is trusted and so is the LSA Process itself. Open the
|
|
// process token
|
|
//
|
|
|
|
Status = NtOpenProcessToken(
|
|
NtCurrentProcess(),
|
|
TOKEN_QUERY,
|
|
&ClientToken
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Build the replacement security descriptor. This must be done in
|
|
// process heap to satisfy the needs of the RTL routine.
|
|
//
|
|
|
|
Status = RtlSetSecurityObject(
|
|
SecurityInformation,
|
|
ModificationSD,
|
|
&SetSD,
|
|
&(LsapDbState.
|
|
DbObjectTypes[InternalHandle->ObjectTypeId].GenericMapping),
|
|
ClientToken
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
SetSDLength = RtlLengthSecurityDescriptor( SetSD );
|
|
|
|
//
|
|
// Now replace the existing SD with the updated one.
|
|
//
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
ObjectHandle,
|
|
&LsapDbNames[SecDesc],
|
|
SetSD,
|
|
SetSDLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
SetSecurityObjectFinish:
|
|
|
|
//
|
|
// If necessary, close the Client Token.
|
|
//
|
|
|
|
if (ClientToken != 0) {
|
|
|
|
SecondaryStatus = NtClose( ClientToken );
|
|
|
|
ClientToken = NULL;
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If necessary, free the buffer containing the retrieved SD.
|
|
//
|
|
|
|
if (RetrieveSD != NULL) {
|
|
|
|
MIDL_user_free( RetrieveSD );
|
|
RetrieveSD = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, dereference the object, finish the database
|
|
// transaction, notify the LSA Database Replicator of the change,
|
|
// release the LSA Database lock and return.
|
|
//
|
|
|
|
if (ObjectReferenced) {
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&ObjectHandle,
|
|
InternalHandle->ObjectTypeId,
|
|
InternalHandle->ObjectTypeId,
|
|
LSAP_DB_LOCK | LSAP_DB_FINISH_TRANSACTION,
|
|
SecurityDbChange,
|
|
Status
|
|
);
|
|
|
|
ObjectReferenced = FALSE;
|
|
}
|
|
|
|
//
|
|
// Free the Security descriptor that we set
|
|
//
|
|
if ( SetSD ) {
|
|
|
|
RtlFreeHeap( RtlProcessHeap(), 0, SetSD );
|
|
}
|
|
|
|
LsarpReturnPrologue();
|
|
|
|
return(Status);
|
|
|
|
SetSecurityObjectError:
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
goto SetSecurityObjectFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbRebuildCache(
|
|
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function rebuilds cached information for a given LSA object type.
|
|
|
|
Arguments:
|
|
|
|
ObjectTypeId - Specifies the Object Type for which the cached information
|
|
is to be rebuilt.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// If caching is not supporte, just return.
|
|
//
|
|
|
|
if (!LsapDbIsCacheSupported( ObjectTypeId )) {
|
|
|
|
goto RebuildCacheFinish;
|
|
}
|
|
|
|
//
|
|
// Call the build routine for the specified LSA object Type
|
|
//
|
|
|
|
switch (ObjectTypeId) {
|
|
|
|
case PolicyObject:
|
|
|
|
SafeAcquireResourceExclusive( &LsapDbState.PolicyCacheLock, TRUE );
|
|
LsapDbMakeCacheInvalid( PolicyObject );
|
|
Status = LsapDbBuildPolicyCache();
|
|
LsapDbMakeCacheValid( PolicyObject );
|
|
SafeReleaseResource( &LsapDbState.PolicyCacheLock );
|
|
break;
|
|
|
|
case AccountObject:
|
|
|
|
LsapDbMakeCacheInvalid( AccountObject );
|
|
Status = LsapDbBuildAccountCache();
|
|
LsapDbMakeCacheValid( AccountObject );
|
|
break;
|
|
|
|
case TrustedDomainObject:
|
|
|
|
LsapDbAcquireWriteLockTrustedDomainList();
|
|
LsapDbMakeCacheInvalid( TrustedDomainObject );
|
|
LsapDbPurgeTrustedDomainCache();
|
|
Status = LsapDbBuildTrustedDomainCache();
|
|
LsapDbMakeCacheValid( TrustedDomainObject );
|
|
LsapDbReleaseLockTrustedDomainList();
|
|
break;
|
|
|
|
case SecretObject:
|
|
|
|
LsapDbMakeCacheInvalid( SecretObject );
|
|
Status = LsapDbBuildSecretCache();
|
|
LsapDbMakeCacheValid( SecretObject );
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto RebuildCacheError;
|
|
}
|
|
|
|
RebuildCacheFinish:
|
|
|
|
return(Status);
|
|
|
|
RebuildCacheError:
|
|
|
|
LsapDbMakeCacheInvalid( ObjectTypeId );
|
|
|
|
goto RebuildCacheFinish;
|
|
}
|
|
|