mirror of https://github.com/lianthony/NT4.0
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.
3965 lines
106 KiB
3965 lines
106 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 "lsasrvp.h"
|
|
#include "dbp.h"
|
|
#include "adtp.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_BACKUP_CONTROLLER_CHECK - Omit the check for a BDC.
|
|
This flag is set usually because an object (e.g. a local secret)
|
|
is local to a specific computer and is not replicated. Objects
|
|
of this type may be created, updated or deleted by non-trusted
|
|
clients on BDC's, so no BDC check is required.
|
|
|
|
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 ContainerSecurityDescriptor = 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;
|
|
LSAP_DB_HANDLE ContainerHandle = NULL;
|
|
|
|
PSECURITY_DESCRIPTOR SavedSecurityDescriptor =
|
|
ObjectInformation->ObjectAttributes.SecurityDescriptor;
|
|
|
|
//
|
|
// Validate the Object Information parameter.
|
|
//
|
|
|
|
Status = LsapDbVerifyInformationObject( ObjectInformation );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenObjectError;
|
|
}
|
|
|
|
//
|
|
// Verify that the Lsa database is now locked.
|
|
//
|
|
|
|
ASSERT (LsapDbIsLocked());
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
OutputHandle = LsapDbCreateHandle( ObjectInformation, Options );
|
|
InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (OutputHandle == NULL) {
|
|
|
|
goto OpenObjectError;
|
|
}
|
|
|
|
//
|
|
// 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; // 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)) {
|
|
|
|
SecurityDescriptorLength = 0;
|
|
|
|
Status = LsapDbReadAttributeObject(
|
|
OutputHandle,
|
|
&LsapDbNames[SecDesc],
|
|
NULL,
|
|
&SecurityDescriptorLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenObjectError;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer from the Lsa Heap for the existing object's SD.
|
|
//
|
|
|
|
SecurityDescriptor = LsapAllocateLsaHeap( SecurityDescriptorLength );
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (SecurityDescriptor == NULL) {
|
|
|
|
goto OpenObjectError;
|
|
}
|
|
|
|
//
|
|
// Read the SD. It is the value of the SecDesc subkey.
|
|
//
|
|
|
|
Status = LsapDbReadAttributeObject(
|
|
OutputHandle,
|
|
&LsapDbNames[SecDesc],
|
|
SecurityDescriptor,
|
|
&SecurityDescriptorLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenObjectError;
|
|
}
|
|
|
|
//
|
|
// Reference the SD read from the LSA Database from the object
|
|
// information.
|
|
//
|
|
|
|
ObjectInformation->ObjectAttributes.SecurityDescriptor =
|
|
SecurityDescriptor;
|
|
|
|
//
|
|
// Request the desired accesses and store them in the object's handle.
|
|
// granted.
|
|
//
|
|
|
|
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)) {
|
|
|
|
goto OpenObjectError;
|
|
}
|
|
}
|
|
|
|
*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) {
|
|
|
|
LsapDbFreeHandle(OutputHandle);
|
|
}
|
|
|
|
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 ULONG TypeSpecificAttributeCount,
|
|
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_ACQUIRE_LOCK - Acquire the LSA Database lock
|
|
|
|
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.
|
|
|
|
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;
|
|
BOOLEAN AcquiredLock = FALSE;
|
|
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;
|
|
|
|
//
|
|
// Verify the creation disposition.
|
|
//
|
|
|
|
if ((CreateDisposition != LSAP_DB_OBJECT_CREATE) &&
|
|
(CreateDisposition != LSAP_DB_OBJECT_OPEN_IF)) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
//
|
|
// Optionally lock the Lsa Database
|
|
//
|
|
|
|
if (Options & LSAP_DB_ACQUIRE_LOCK) {
|
|
|
|
Status = LsapDbAcquireLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
}
|
|
|
|
AcquiredLock = TRUE;
|
|
|
|
//
|
|
// Try to open the object. It is permissible for the object to
|
|
// exist already if LSA_OBJECT_OPEN_IF disposition was specified.
|
|
//
|
|
|
|
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;
|
|
|
|
Status = STATUS_OBJECT_NAME_EXISTS;
|
|
|
|
if (CreateDisposition == LSAP_DB_OBJECT_OPEN_IF) {
|
|
|
|
goto CreateObjectFinish;
|
|
}
|
|
|
|
Status = STATUS_OBJECT_NAME_COLLISION;
|
|
|
|
if (CreateDisposition == LSAP_DB_OBJECT_CREATE) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
OutputHandle = LsapDbCreateHandle( ObjectInformation, Options );
|
|
InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (OutputHandle == NULL) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
Status = LsapDbOpenTransaction();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateObjectError;
|
|
}
|
|
|
|
OpenedTransaction = TRUE;
|
|
|
|
//
|
|
// Add a registry transaction to create the Registry key for the new
|
|
// Database object.
|
|
//
|
|
|
|
Status = RtlAddActionToRXact(
|
|
LsapDbState.RXactContext,
|
|
RtlRXactOperationSetValue,
|
|
&InternalOutputHandle->PhysicalNameU,
|
|
ObjectTypeId,
|
|
NULL, // No Key Value needed
|
|
0L
|
|
);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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 | LSAP_DB_FINISH_TRANSACTION,
|
|
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;
|
|
|
|
//
|
|
// 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 (LsapDbIsCacheSupported( ObjectTypeId)) {
|
|
|
|
if (LsapDbIsCacheValid( ObjectTypeId)) {
|
|
|
|
switch (ObjectTypeId) {
|
|
|
|
case AccountObject:
|
|
|
|
IgnoreStatus = LsapDbCreateAccount(
|
|
InternalOutputHandle->Sid,
|
|
NULL
|
|
);
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CreateObjectFinish:
|
|
|
|
//
|
|
// Return NULL or a handle to the newly created and opened object.
|
|
//
|
|
|
|
*ObjectHandle = OutputHandle;
|
|
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) {
|
|
|
|
Status = LsapDbResetStates(
|
|
OutputHandle,
|
|
LSAP_DB_FINISH_TRANSACTION,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
}
|
|
|
|
//
|
|
// If we opened the object, close it.
|
|
//
|
|
|
|
if (OpenedObject) {
|
|
|
|
CloseOptions = 0;
|
|
SecondaryStatus = LsapDbCloseObject( &OutputHandle, CloseOptions );
|
|
|
|
if (!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.
|
|
//
|
|
|
|
LsapDbFreeHandle( OutputHandle );
|
|
|
|
OutputHandle = NULL;
|
|
InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
|
|
}
|
|
|
|
goto CreateObjectFinish;
|
|
|
|
DBG_UNREFERENCED_PARAMETER( CloseOptions );
|
|
}
|
|
|
|
|
|
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
|
|
|
|
LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK - Omit the check for a BDC for
|
|
a create/update/delete operation on a local (non-replicated)
|
|
object such as a local secret.
|
|
|
|
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, RevertStatus, AccessStatus;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
|
|
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = InternalHandle->ObjectTypeId;
|
|
BOOLEAN WriteOperation = FALSE;
|
|
ULONG EffectiveOptions = Options | InternalHandle->Options;
|
|
|
|
//
|
|
// If the system is a Backup Domain Controller, disallow update
|
|
// operations for non-trusted callers except in special cases such
|
|
// as local non-replicated objects. In these special cases, the
|
|
// flag LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK will be already set
|
|
// in the handle Options.
|
|
//
|
|
|
|
WriteOperation = RtlAreAnyAccessesGranted(
|
|
LsapDbState.DbObjectTypes[InternalHandle->ObjectTypeId].WriteOperations,
|
|
DesiredAccess
|
|
);
|
|
|
|
if ((LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole == PolicyServerRoleBackup) &&
|
|
(!InternalHandle->Trusted) &&
|
|
WriteOperation &&
|
|
(!(EffectiveOptions & LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK))) {
|
|
|
|
Status = STATUS_BACKUP_CONTROLLER;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
Status = I_RpcMapWin32Status(RpcImpersonateClient(0));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Map any Generic Access Types to Specific Access Types
|
|
//
|
|
|
|
RtlMapGenericMask(
|
|
&DesiredAccess,
|
|
&(LsapDbState.DbObjectTypes[ObjectTypeId].GenericMapping)
|
|
);
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
Status = NtAccessCheckAndAuditAlarm(
|
|
&LsapState.SubsystemName,
|
|
ObjectHandle,
|
|
&LsapDbObjectTypeNames[ObjectTypeId],
|
|
(PUNICODE_STRING) ObjectInformation->ObjectAttributes.ObjectName,
|
|
ObjectInformation->ObjectAttributes.SecurityDescriptor,
|
|
DesiredAccess,
|
|
&(LsapDbState.DbObjectTypes[ObjectTypeId].GenericMapping),
|
|
FALSE,
|
|
(PACCESS_MASK) &(InternalHandle->GrantedAccess),
|
|
(PNTSTATUS) &AccessStatus,
|
|
(PBOOLEAN) &(InternalHandle->GenerateOnClose)
|
|
);
|
|
|
|
//
|
|
// Before checking the Status, stop impersonating the client and become
|
|
// our former self.
|
|
//
|
|
|
|
RevertStatus = I_RpcMapWin32Status(RpcRevertToSelf());
|
|
|
|
if (!NT_SUCCESS(RevertStatus)) {
|
|
|
|
LsapLogError(
|
|
"LsapDbRequestAccessObject: RpcRevertToSelf failed 0x%lx\n",
|
|
Status
|
|
);
|
|
}
|
|
|
|
//
|
|
// 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
|
|
|
|
LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK - Omit the check for a BDC for
|
|
a create/update/delete operation on a local (non-replicated)
|
|
object such as a local secret.
|
|
|
|
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;
|
|
BOOLEAN WriteOperation = FALSE;
|
|
ULONG EffectiveOptions = Options | InternalHandle->Options;
|
|
|
|
//
|
|
// If the system is a Backup Domain Controller, disallow update
|
|
// operations for non-trusted callers except in special cases such
|
|
// as local non-replicated objects. In these special cases, the
|
|
// flag LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK will be already set
|
|
// in the handle Options.
|
|
//
|
|
|
|
WriteOperation = RtlAreAnyAccessesGranted(
|
|
LsapDbState.DbObjectTypes[ObjectTypeId].WriteOperations,
|
|
EffectiveDesiredAccess
|
|
);
|
|
|
|
if ((LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole == PolicyServerRoleBackup) &&
|
|
(!InternalHandle->Trusted) &&
|
|
WriteOperation &&
|
|
(!(EffectiveOptions & LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK))) {
|
|
|
|
Status = STATUS_BACKUP_CONTROLLER;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// 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,
|
|
TRUE,
|
|
SE_SECURITY_PRIVILEGE,
|
|
NULL
|
|
);
|
|
|
|
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
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
WARNING: The Lsa Database must be in the locked state when this function
|
|
is called.
|
|
|
|
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_FREE_HANDLE - Free the handle whether or not the
|
|
Reference Count reaches zero.
|
|
|
|
LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES - Permit the handle provided
|
|
to be for a deleted object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Verify that the LSA Database is locked
|
|
//
|
|
|
|
ASSERT (LsapDbIsLocked());
|
|
|
|
//
|
|
// 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,
|
|
Options,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
|
|
*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];
|
|
PUNICODE_STRING *NextAttributeName;
|
|
ULONG AttributeCount;
|
|
ULONG AttributeNumber;
|
|
LSAPR_TRUST_INFORMATION TrustInformation;
|
|
|
|
//
|
|
// Verify that the LSA Database is locked.
|
|
//
|
|
|
|
ASSERT (LsapDbIsLocked());
|
|
|
|
//
|
|
// 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:
|
|
|
|
*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 = LsapDbDeleteTrustedDomainList( NULL, &TrustInformation );
|
|
|
|
break;
|
|
|
|
case AccountObject:
|
|
|
|
*NextAttributeName = &LsapDbNames[Sid];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[ActSysAc];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[Privilgs];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*NextAttributeName = &LsapDbNames[QuotaLim];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
break;
|
|
|
|
case SecretObject:
|
|
|
|
*NextAttributeName = &LsapDbNames[CurrVal];
|
|
NextAttributeName++;
|
|
AttributeCount++;
|
|
|
|
*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; AttributeNumber++) {
|
|
|
|
Status = LsapDbDeleteAttributeObject(
|
|
ObjectHandle,
|
|
AttributeNames[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.
|
|
//
|
|
|
|
Status = NtClose(Handle->KeyHandle);
|
|
|
|
Handle->KeyHandle = NULL;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteObjectError;
|
|
}
|
|
|
|
//
|
|
// Add a Registry Transaction to delete the object's Registry Key.
|
|
//
|
|
|
|
Status = RtlAddActionToRXact(
|
|
LsapDbState.RXactContext,
|
|
RtlRXactOperationDelete,
|
|
&((LSAP_DB_HANDLE) ObjectHandle)->PhysicalNameU,
|
|
0L,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteObjectError;
|
|
}
|
|
|
|
DeleteObjectFinish:
|
|
|
|
//
|
|
// 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 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.
|
|
|
|
ObjectTypeId - 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.
|
|
|
|
Options - Specifies optional additional actions including database state
|
|
changes to be made, or actions not to be performed.
|
|
|
|
LSAP_DB_ACQUIRE_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_ACQUIRE_LOG_QUEUE_LOCK - Acquire the Lsa Audit Log Queue
|
|
Lock.
|
|
|
|
LSAP_DB_START_TRANSACTION - Start an Lsa database transaction.
|
|
|
|
LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK - Omit check that local system
|
|
is not a Backup Domain Controller.
|
|
|
|
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 WriteOperation;
|
|
|
|
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.
|
|
//
|
|
|
|
if (States != 0) {
|
|
|
|
Status = LsapDbSetStates( States );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto ReferenceError;
|
|
}
|
|
|
|
if (States & LSAP_DB_START_TRANSACTION) {
|
|
|
|
ResetStates |= LSAP_DB_FINISH_TRANSACTION;
|
|
}
|
|
|
|
if (States & LSAP_DB_ACQUIRE_LOCK) {
|
|
|
|
ResetStates |= LSAP_DB_RELEASE_LOCK;
|
|
}
|
|
|
|
if (States & LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK) {
|
|
|
|
ResetStates |= LSAP_DB_RELEASE_LOG_QUEUE_LOCK;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
Status = LsapDbVerifyHandle( ObjectHandle, 0, ObjectTypeId );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto ReferenceError;
|
|
}
|
|
|
|
//
|
|
// There may also be options set in the handle. Take these into
|
|
// account as well.
|
|
//
|
|
|
|
EffectiveOptions = Options | InternalHandle->Options;
|
|
|
|
//
|
|
// If the system is a Backup Domain Controller, disallow update
|
|
// operations for non-trusted callers except in special cases such
|
|
// as local non-replicated objects. In these special cases where update
|
|
// is allowed, the flag LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK will be already set
|
|
// in the handle Options.
|
|
//
|
|
|
|
WriteOperation = RtlAreAnyAccessesGranted(
|
|
LsapDbState.DbObjectTypes[InternalHandle->ObjectTypeId].WriteOperations,
|
|
DesiredAccess
|
|
);
|
|
|
|
if ((LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole == PolicyServerRoleBackup) &&
|
|
(!InternalHandle->Trusted) &&
|
|
WriteOperation &&
|
|
(!(EffectiveOptions & LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK))) {
|
|
|
|
Status = STATUS_BACKUP_CONTROLLER;
|
|
goto ReferenceError;
|
|
}
|
|
|
|
//
|
|
// If the handle is not Trusted, verify that the desired accesses have been granted
|
|
//
|
|
|
|
if (!(InternalHandle->Trusted)) {
|
|
|
|
if (!RtlAreAllAccessesGranted( InternalHandle->GrantedAccess, DesiredAccess )) {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto ReferenceError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reference the handle
|
|
//
|
|
|
|
if (InternalHandle->ReferenceCount == LSAP_DB_MAXIMUM_REFERENCE_COUNT) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReferenceError;
|
|
}
|
|
|
|
InternalHandle->ReferenceCount++;
|
|
return (Status);
|
|
|
|
ReferenceError:
|
|
|
|
//
|
|
// Unset the states in the correct order. If a database transaction
|
|
// was started by this routine, it will be aborted.
|
|
//
|
|
|
|
Status = LsapDbResetStates(
|
|
ObjectHandle,
|
|
ResetStates,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbDereferenceObject(
|
|
IN OUT PLSAPR_HANDLE ObjectHandle,
|
|
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.
|
|
|
|
ObjectTypeId - Expected type of object. This parameter is ignored
|
|
if ValidateHandle is set to FALSE.
|
|
|
|
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_FREE_HANDLE - Free the handle whether or not the
|
|
Reference Count reaches zero. If LSAP_DB_DEREFERENCE_CONTR
|
|
is also specified, the container handle Reference Count is
|
|
decremented by the reference count in the handle being deleted.
|
|
|
|
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_RELEASE_LOCK - The Lsa database lock was acquired and
|
|
should be released.
|
|
|
|
LSAP_DB_RELEASE_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;
|
|
|
|
Status = PreliminaryStatus;
|
|
SecondaryStatus = STATUS_SUCCESS;
|
|
|
|
|
|
ASSERT (LsapDbIsLocked());
|
|
|
|
//
|
|
// 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,
|
|
ObjectTypeId
|
|
);
|
|
|
|
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.
|
|
//
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
(PLSAPR_HANDLE) &InternalHandle->ContainerHandle,
|
|
NullObject,
|
|
0,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
DereferenceObjectFinish:
|
|
|
|
//
|
|
// Decrement the Reference Count. If it becomes zero, free the
|
|
// handle. If explicitly requested to free the handle (regardless of
|
|
// the Reference Count), force the Reference Count to zero prior to
|
|
// freeing.
|
|
//
|
|
|
|
if (DecrementCount) {
|
|
|
|
if (Options & LSAP_DB_FREE_HANDLE) {
|
|
|
|
InternalHandle->ReferenceCount = (ULONG) 1;
|
|
}
|
|
|
|
(InternalHandle->ReferenceCount)--;
|
|
ReferenceCount = InternalHandle->ReferenceCount;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// This must happen after the reference count is adjusted, as it the one
|
|
// that will unlock the database.
|
|
//
|
|
|
|
if (NT_SUCCESS(SecondaryStatus))
|
|
{
|
|
Status = LsapDbResetStates(
|
|
*ObjectHandle,
|
|
EffectiveOptions,
|
|
SecurityDbDeltaType,
|
|
Status
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// This has to happen after resetting states because resetting
|
|
// requires the handle to be present.
|
|
//
|
|
|
|
if (DecrementCount && (ReferenceCount == 0)) {
|
|
|
|
TmpStatus = NtCloseObjectAuditAlarm (
|
|
&LsapState.SubsystemName,
|
|
*ObjectHandle,
|
|
InternalHandle->GenerateOnClose
|
|
);
|
|
|
|
if (!NT_SUCCESS( TmpStatus )) {
|
|
LsapAuditFailed();
|
|
}
|
|
|
|
LsapDbFreeHandle( *ObjectHandle );
|
|
|
|
*ObjectHandle = NULL;
|
|
}
|
|
|
|
|
|
|
|
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, SecondaryStatus;
|
|
ULONG SubKeyValueActualLength;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE SubKeyHandle = NULL;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
|
|
|
|
//
|
|
// Verify that the LSA Database is locked
|
|
//
|
|
|
|
ASSERT (LsapDbIsLocked());
|
|
|
|
//
|
|
// Reading an attribute of an object is simpler than writing one,
|
|
// because the Registry Transaction package is not used. Since an
|
|
// attribute is stored as the value of a subkey of the object's
|
|
// Registry Key, we can simply call the Registry API RtlpNtReadKey
|
|
// specifying the relative name of the subkey and the parent key's
|
|
// handle.
|
|
//
|
|
// Prior to opening the subkey in the Registry, setup ObjectAttributes
|
|
// containing the SubKey name and the Registry Handle for the LSA Database
|
|
// Root.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
AttributeNameU,
|
|
OBJ_CASE_INSENSITIVE,
|
|
InternalHandle->KeyHandle,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the subkey
|
|
//
|
|
|
|
Status = RtlpNtOpenKey(
|
|
&SubKeyHandle,
|
|
KEY_READ,
|
|
&ObjectAttributes,
|
|
0L
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
SubKeyHandle = NULL; //For error processing
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Now query the size of the buffer required to read the subkey's
|
|
// value.
|
|
//
|
|
|
|
SubKeyValueActualLength = *AttributeValueLength;
|
|
|
|
Status = RtlpNtQueryValueKey(
|
|
SubKeyHandle,
|
|
NULL,
|
|
NULL,
|
|
&SubKeyValueActualLength,
|
|
NULL
|
|
);
|
|
|
|
if ((Status == STATUS_BUFFER_OVERFLOW) || NT_SUCCESS(Status)) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
goto ReadAttError;
|
|
}
|
|
|
|
//
|
|
// If a NULL buffer parameter has been supplied or the size of the
|
|
// buffer given is 0, this is just a size query.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(AttributeValue) || *AttributeValueLength == 0) {
|
|
|
|
*AttributeValueLength = SubKeyValueActualLength;
|
|
Status = STATUS_SUCCESS;
|
|
goto ReadAttError;
|
|
|
|
} else if(*AttributeValueLength < SubKeyValueActualLength) {
|
|
|
|
*AttributeValueLength = SubKeyValueActualLength;
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
goto ReadAttError;
|
|
}
|
|
|
|
//
|
|
// Supplied buffer is large enough to hold the SubKey's value.
|
|
// Query the value.
|
|
//
|
|
|
|
Status = RtlpNtQueryValueKey(
|
|
SubKeyHandle,
|
|
NULL,
|
|
AttributeValue,
|
|
&SubKeyValueActualLength,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto ReadAttError;
|
|
}
|
|
|
|
//
|
|
// Return the length of the Sub Key.
|
|
//
|
|
|
|
*AttributeValueLength = SubKeyValueActualLength;
|
|
|
|
ReadAttFinish:
|
|
|
|
//
|
|
// If necessary, close the Sub Key
|
|
//
|
|
|
|
if (SubKeyHandle != NULL) {
|
|
|
|
SecondaryStatus = NtClose( SubKeyHandle );
|
|
|
|
#if DBG
|
|
|
|
if (!NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
DbgPrint(
|
|
"LsapDbReadAttributeObject: NtClose failed 0x%lx\n",
|
|
Status
|
|
);
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
}
|
|
|
|
return(Status);
|
|
|
|
ReadAttError:
|
|
|
|
goto ReadAttFinish;
|
|
}
|
|
|
|
|
|
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
|
|
//
|
|
|
|
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 = RtlAddActionToRXact(
|
|
LsapDbState.RXactContext,
|
|
RtlRXactOperationSetValue,
|
|
&PhysicalSubKeyNameU,
|
|
0L,
|
|
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;
|
|
|
|
for(Index = 0; Index < AttributeCount; Index++) {
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
ObjectHandle,
|
|
Attributes[Index].AttributeName,
|
|
Attributes[Index].AttributeValue,
|
|
Attributes[Index].AttributeValueLength
|
|
);
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbReadAttributesObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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
|
|
)
|
|
|
|
/*++
|
|
|
|
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
|
|
//
|
|
|
|
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 = RtlAddActionToRXact(
|
|
LsapDbState.RXactContext,
|
|
RtlRXactOperationDelete,
|
|
&PhysicalSubKeyNameU,
|
|
0L,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
RtlFreeUnicodeString(&PhysicalSubKeyNameU);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto DeleteAttributeObjectError;
|
|
}
|
|
|
|
DeleteAttributeObjectFinish:
|
|
|
|
return(Status);
|
|
|
|
DeleteAttributeObjectError:
|
|
|
|
//
|
|
// Add any cleanup required on error paths only here.
|
|
//
|
|
|
|
goto DeleteAttributeObjectFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbReferencesObject(
|
|
IN LSAPR_HANDLE ObjectHandle,
|
|
OUT PULONG ReferenceCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the Reference Count for the object. This is
|
|
the sum of the Reference Counts found in each open handle. The LSA
|
|
Database must be locked before calling this function.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Handle to the object.
|
|
|
|
ReferenceCount - Receives the Reference Count for the object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_INVALID_HANDLE - Specified handle is invalid.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Verify that the Lsa Database is locked.
|
|
//
|
|
|
|
ASSERT (LsapDbIsLocked());
|
|
|
|
Status = LsapDbReferencesHandle( ObjectHandle, ReferenceCount );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
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;
|
|
UNICODE_STRING ObjectName;
|
|
PSID ObjectSid = NULL;
|
|
ULONG ObjectRid = 0;
|
|
UCHAR SubAuthorityCount;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
|
|
BOOLEAN ReplicateImmediately = FALSE;
|
|
|
|
ObjectName.Buffer = NULL;
|
|
ObjectName.Length = ObjectName.MaximumLength = 0;
|
|
|
|
//
|
|
// If notifications are disabled, just exit.
|
|
//
|
|
|
|
if (!LsapDbState.ReplicatorNotificationEnabled) {
|
|
|
|
goto NotifyChangeObjectFinish;
|
|
}
|
|
|
|
//
|
|
// If the system is a Backup Domain Controller, don't notify the
|
|
// replicator of any changes.
|
|
//
|
|
|
|
if (LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole == PolicyServerRoleBackup) {
|
|
|
|
goto NotifyChangeObjectFinish;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto NotifyChangeObjectError;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
SubAuthorityCount = *RtlSubAuthorityCountSid( ObjectSid );
|
|
ObjectRid = *RtlSubAuthoritySid( ObjectSid, SubAuthorityCount -1 );
|
|
|
|
} else if (LsapDbAccessedByNameObject( InternalHandle->ObjectTypeId )) {
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
NULL,
|
|
&ObjectName,
|
|
&InternalHandle->LogicalNameU
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto NotifyChangeObjectError;
|
|
}
|
|
|
|
} 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 NotifyChangeObjectError;
|
|
}
|
|
|
|
//
|
|
// Notify the LSA Database Replicator of the change.
|
|
//
|
|
|
|
Status = I_NetNotifyDelta (
|
|
SecurityDbLsa,
|
|
LsapDbState.PolicyModificationInfo.ModifiedId,
|
|
SecurityDbDeltaType,
|
|
ObjectType,
|
|
ObjectRid,
|
|
ObjectSid,
|
|
&ObjectName,
|
|
ReplicateImmediately,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto NotifyChangeObjectError;
|
|
}
|
|
|
|
NotifyChangeObjectFinish:
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
return(Status);
|
|
|
|
NotifyChangeObjectError:
|
|
|
|
goto NotifyChangeObjectFinish;
|
|
}
|
|
|
|
|
|
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,
|
|
OUT OPTIONAL PUNICODE_STRING LogicalNameU,
|
|
OUT OPTIONAL PUNICODE_STRING PhysicalNameU
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
//
|
|
// Initialize
|
|
//
|
|
|
|
RtlInitUnicodeString( &IntermediatePath1U, NULL );
|
|
RtlInitUnicodeString( &TempLogicalNameU, NULL );
|
|
|
|
//
|
|
// Verify that the LSA Database is locked
|
|
//
|
|
|
|
ASSERT (LsapDbIsLocked());
|
|
|
|
//
|
|
// Capture the Logical Name of the object into permanent memory.
|
|
//
|
|
|
|
Status = LsapRtlCopyUnicodeString(
|
|
&TempLogicalNameU,
|
|
(PUNICODE_STRING)
|
|
ObjectInformation->ObjectAttributes.ObjectName,
|
|
TRUE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto GetNamesError;
|
|
}
|
|
|
|
//
|
|
// 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)) {
|
|
|
|
goto GetNamesFinish;
|
|
}
|
|
|
|
//
|
|
// The Physical Name of the object is requested. Construct this
|
|
// in stages. First, get the Container Object Physical Name from
|
|
// the handle stored inside ObjectAttributes.
|
|
//
|
|
|
|
if (ObjectAttributes->RootDirectory != NULL) {
|
|
|
|
ContainerPhysicalNameU =
|
|
&(((LSAP_DB_HANDLE)
|
|
ObjectAttributes->RootDirectory)->PhysicalNameU);
|
|
}
|
|
|
|
//
|
|
// Next, get the Classifying Directory name appropriate to the
|
|
// object type.
|
|
//
|
|
|
|
if (LsapDbContDirs[ObjectTypeId].Length != 0) {
|
|
|
|
ClassifyingDirU = &LsapDbContDirs[ObjectTypeId];
|
|
}
|
|
|
|
//
|
|
// Now join the Physical Name of the Container Object and Classifying
|
|
// Directory together. If there is no Container Object and no
|
|
// Classifying Directory, just set the result to NULL.
|
|
//
|
|
|
|
if (ContainerPhysicalNameU == NULL && ClassifyingDirU == NULL) {
|
|
|
|
JoinedPath1U = NULL;
|
|
|
|
} else {
|
|
|
|
Status = LsapDbJoinSubPaths(
|
|
ContainerPhysicalNameU,
|
|
ClassifyingDirU,
|
|
JoinedPath1U
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto GetNamesError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now join the Physical Name of the Containing Object, Classifying
|
|
// Directory and Logical Name of the object together. Note that
|
|
// JoinedPath1U may be NULL, but LogicalNameU is never NULL.
|
|
//
|
|
|
|
Status = LsapDbJoinSubPaths(
|
|
JoinedPath1U,
|
|
&TempLogicalNameU,
|
|
PhysicalNameU
|
|
);
|
|
if (JoinedPath1U != NULL) {
|
|
|
|
RtlFreeUnicodeString( JoinedPath1U );
|
|
JoinedPath1U = NULL; // so we don't try to free it again
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto GetNamesError;
|
|
}
|
|
|
|
goto GetNamesFinish;
|
|
|
|
GetNamesError:
|
|
|
|
//
|
|
// If necessary, free any string buffer allocated for the Logical Name
|
|
//
|
|
|
|
RtlFreeUnicodeString( &TempLogicalNameU );
|
|
|
|
//
|
|
// If necessary, free any string buffer allocated to JoinedPath1U
|
|
//
|
|
|
|
if (JoinedPath1U != NULL) {
|
|
|
|
RtlFreeUnicodeString( JoinedPath1U );
|
|
}
|
|
|
|
GetNamesFinish:
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
LsapDbIsLocked()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if LSA Database is locked.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if LSA database is locked, else false.
|
|
|
|
--*/
|
|
|
|
{
|
|
return (BOOLEAN)(LsapDbState.DbLock.LockCount != -1L);
|
|
}
|
|
|
|
|
|
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
|
|
*SD,
|
|
*ReturnSD;
|
|
|
|
ULONG
|
|
ReturnSDLength;
|
|
|
|
|
|
|
|
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,
|
|
NullObject,
|
|
LSAP_DB_ACQUIRE_LOCK
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
|
|
//
|
|
// Read the existing Security Descriptor for the object. This always
|
|
// exists as the value of the SecDesc attribute of the object.
|
|
//
|
|
|
|
LsapDbInitializeAttribute(
|
|
&Attribute,
|
|
&LsapDbNames[ SecDesc ],
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
Status = LsapDbReadAttribute( ObjectHandle, &Attribute );
|
|
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
SD = Attribute.AttributeValue;
|
|
ASSERT( SD != NULL );
|
|
|
|
|
|
//
|
|
// Elimate components that weren't requested.
|
|
//
|
|
|
|
if ( !(SecurityInformation & OWNER_SECURITY_INFORMATION)) {
|
|
SD->Owner = NULL;
|
|
}
|
|
|
|
if ( !(SecurityInformation & GROUP_SECURITY_INFORMATION)) {
|
|
SD->Group = NULL;
|
|
}
|
|
|
|
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,
|
|
(PSECURITY_DESCRIPTOR) 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;
|
|
} else {
|
|
Status = RtlMakeSelfRelativeSD( (PSECURITY_DESCRIPTOR) SD,
|
|
(PSECURITY_DESCRIPTOR) ReturnSD,
|
|
&ReturnSDLength );
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
}
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Allocate the first block of returned memory.
|
|
//
|
|
|
|
RpcSD = MIDL_user_allocate( sizeof(LSAPR_SR_SECURITY_DESCRIPTOR) );
|
|
|
|
if (RpcSD == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
if (ReturnSD != NULL) {
|
|
MIDL_user_free( ReturnSD );
|
|
}
|
|
} else {
|
|
|
|
RpcSD->Length = ReturnSDLength;
|
|
RpcSD->SecurityDescriptor = (PUCHAR)( (PVOID)ReturnSD );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// free the attribute read from disk
|
|
//
|
|
|
|
MIDL_user_free( SD );
|
|
}
|
|
|
|
IgnoreStatus = LsapDbDereferenceObject(
|
|
&ObjectHandle,
|
|
InternalHandle->ObjectTypeId,
|
|
LSAP_DB_RELEASE_LOCK,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
ASSERT( NT_SUCCESS(IgnoreStatus) );
|
|
}
|
|
|
|
|
|
*SecurityDescriptor = RpcSD;
|
|
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.
|
|
--*/
|
|
|
|
{
|
|
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;
|
|
|
|
//
|
|
// Verify that a Security Descriptor has been passed.
|
|
//
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
if (!ARGUMENT_PRESENT( SecurityDescriptor )) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT( SecurityDescriptor->SecurityDescriptor )) {
|
|
|
|
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,
|
|
NullObject,
|
|
LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
ObjectReferenced = TRUE;
|
|
|
|
//
|
|
// Read the existing Security Descriptor for the object. This always
|
|
// exists as the value of the SecDesc attribute of the object.
|
|
//
|
|
|
|
LsapDbInitializeAttribute(
|
|
&Attribute,
|
|
&LsapDbNames[ 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;
|
|
|
|
Status = STATUS_INTERNAL_DB_CORRUPTION;
|
|
|
|
if (RetrieveSD == NULL) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
if (RetrieveSDLength == 0) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
SetSD = RtlAllocateHeap( RtlProcessHeap(), 0, RetrieveSDLength );
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (SetSD == NULL) {
|
|
|
|
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,
|
|
LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
|
|
SecurityDbChange,
|
|
Status
|
|
);
|
|
|
|
ObjectReferenced = FALSE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Disable caching
|
|
//
|
|
|
|
LsapDbMakeCacheInvalid( ObjectTypeId );
|
|
|
|
//
|
|
// Call the build routine for the specified LSA object Type
|
|
//
|
|
|
|
switch (ObjectTypeId) {
|
|
|
|
case PolicyObject:
|
|
|
|
Status = LsapDbBuildPolicyCache();
|
|
break;
|
|
|
|
case AccountObject:
|
|
|
|
Status = LsapDbBuildAccountCache();
|
|
break;
|
|
|
|
case TrustedDomainObject:
|
|
|
|
Status = LsapDbBuildTrustedDomainCache();
|
|
break;
|
|
|
|
case SecretObject:
|
|
|
|
Status = LsapDbBuildSecretCache();
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto RebuildCacheError;
|
|
}
|
|
|
|
//
|
|
// Enable caching.
|
|
//
|
|
|
|
LsapDbMakeCacheValid(ObjectTypeId);
|
|
|
|
RebuildCacheFinish:
|
|
|
|
return(Status);
|
|
|
|
RebuildCacheError:
|
|
|
|
//
|
|
// Disable caching until the next reboot.
|
|
//
|
|
|
|
LsapDbMakeCacheUnsupported( ObjectTypeId );
|
|
LsapDbMakeCacheInvalid( ObjectTypeId );
|
|
goto RebuildCacheFinish;
|
|
}
|