/*++ 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\\ \Domains\\ \Secrets\\ \ 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. = [ "\"] [ "\"] If there is no Container Object (as in the case of the Policy object) the and following \ are omitted. If there is no Classifying Directory (as in the case of the Policy object) the 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; }