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.
1750 lines
46 KiB
1750 lines
46 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dbsecret.c
|
|
|
|
Abstract:
|
|
|
|
LSA - Database - Secret Object Private API Workers
|
|
|
|
NOTE: This module should remain as portable code that is independent
|
|
of the implementation of the LSA Database. As such, it is
|
|
permitted to use only the exported LSA Database interfaces
|
|
contained in db.h and NOT the private implementation
|
|
dependent functions in dbp.h.
|
|
|
|
Author:
|
|
|
|
Scott Birrell (ScottBi) December 12, 1991
|
|
|
|
Environment:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "lsasrvp.h"
|
|
#include "dbp.h"
|
|
|
|
|
|
NTSTATUS
|
|
LsarCreateSecret(
|
|
IN LSAPR_HANDLE PolicyHandle,
|
|
IN PLSAPR_UNICODE_STRING SecretName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PLSAPR_HANDLE SecretHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the LSA server RPC worker routine for the
|
|
LsaCreateSecret API.
|
|
|
|
The LsaCreateSecret API creates a named Secret object in the
|
|
Lsa Database. Each Secret Object can have two values assigned,
|
|
called the Current Value and the Old Value. The meaning of these
|
|
values is known to the Secret object creator. The caller must have
|
|
LSA_CREATE_SECRET access to the LsaDatabase object.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
SecretName - Pointer to Unicode String specifying the name of the
|
|
secret.
|
|
|
|
DesiredAccess - Specifies the accesses to be granted to the newly
|
|
created and opened secret.
|
|
|
|
SecretHandle - Receives a handle to the newly created and opened
|
|
Secret object. This handle is used on subsequent accesses to
|
|
the object until closed.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_OBJECT_NAME_COLLISION - A Secret object having the given name
|
|
already exists.
|
|
|
|
STATUS_TOO_MANY_SECRETS - The maximum number of Secret objects in the
|
|
system has been reached.
|
|
|
|
STATUS_NAME_TOO_LONG - The name of the secret is too long to be stored
|
|
in the LSA database.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_OBJECT_INFORMATION ObjectInformation;
|
|
BOOLEAN ContainerReferenced = FALSE;
|
|
LSAP_DB_ATTRIBUTE Attributes[2];
|
|
ULONG TypeSpecificAttributeCount;
|
|
LARGE_INTEGER CreationTime;
|
|
ULONG Index;
|
|
ULONG CreateOptions = (ULONG) 0;
|
|
ULONG ReferenceOptions = (ULONG) LSAP_DB_ACQUIRE_LOCK;
|
|
ULONG DereferenceOptions = (ULONG) LSAP_DB_RELEASE_LOCK;
|
|
BOOLEAN GlobalSecret = FALSE;
|
|
|
|
|
|
//
|
|
// Check to see if the lenght of the name is within the limits of the
|
|
// LSA database.
|
|
//
|
|
|
|
if ( SecretName->Length > LSAP_DB_LOGICAL_NAME_MAX_LENGTH ) {
|
|
return(STATUS_NAME_TOO_LONG);
|
|
}
|
|
|
|
//
|
|
// Check for Local Secret creation request. If the Secret name does
|
|
// not begin with the Global Secret Prefix, the Secret is local. In
|
|
// this case, creation of the secret is allowed on BDC's as well as
|
|
// PDC's and Workstations. Creation of Global Secrets is not
|
|
// allowed on BDC's except for trusted callers such as a Replicator.
|
|
//
|
|
|
|
Status = LsapDbGetScopeSecret( SecretName, &GlobalSecret );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateSecretError;
|
|
}
|
|
|
|
if (!GlobalSecret) {
|
|
|
|
CreateOptions |= (LSAP_DB_OMIT_REPLICATOR_NOTIFICATION |
|
|
LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK);
|
|
ReferenceOptions |= (LSAP_DB_OMIT_REPLICATOR_NOTIFICATION |
|
|
LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK);
|
|
}
|
|
|
|
//
|
|
// Acquire the Lsa Database lock. Verify that the connection handle
|
|
// (container object handle) is valid, is of the expected type and has
|
|
// all of the desired accesses granted. Reference the container
|
|
// object handle.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
PolicyHandle,
|
|
POLICY_CREATE_SECRET,
|
|
PolicyObject,
|
|
ReferenceOptions
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateSecretError;
|
|
}
|
|
|
|
ContainerReferenced = TRUE;
|
|
|
|
//
|
|
// Fill in the ObjectInformation structure. Initialize the
|
|
// embedded Object Attributes with the PolicyHandle as the
|
|
// Root Directory (Container Object) handle and the Logical Name
|
|
// of the account. Store the types of the object and its container.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectInformation.ObjectAttributes,
|
|
(PUNICODE_STRING)SecretName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
PolicyHandle,
|
|
NULL
|
|
);
|
|
|
|
ObjectInformation.ObjectTypeId = SecretObject;
|
|
ObjectInformation.ContainerTypeId = PolicyObject;
|
|
ObjectInformation.Sid = NULL;
|
|
|
|
//
|
|
// Set up the Creation Time as a Type Specific Attribute.
|
|
//
|
|
|
|
Status = NtQuerySystemTime(&CreationTime);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateSecretError;
|
|
}
|
|
|
|
Index = 0;
|
|
|
|
Attributes[Index].AttributeName = &LsapDbNames[CupdTime];
|
|
Attributes[Index].AttributeValue = &CreationTime;
|
|
Attributes[Index].AttributeValueLength = sizeof (LARGE_INTEGER);
|
|
Index++;
|
|
|
|
Attributes[Index].AttributeName = &LsapDbNames[OupdTime];
|
|
Attributes[Index].AttributeValue = &CreationTime;
|
|
Attributes[Index].AttributeValueLength = sizeof (LARGE_INTEGER);
|
|
Index++;
|
|
|
|
TypeSpecificAttributeCount = Index;
|
|
|
|
//
|
|
// Create the Secret Object. We fail if the object already exists.
|
|
// Note that the object create routine performs a Database transaction.
|
|
//
|
|
|
|
Status = LsapDbCreateObject(
|
|
&ObjectInformation,
|
|
DesiredAccess,
|
|
LSAP_DB_OBJECT_CREATE,
|
|
CreateOptions,
|
|
Attributes,
|
|
TypeSpecificAttributeCount,
|
|
SecretHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto CreateSecretError;
|
|
}
|
|
|
|
CreateSecretFinish:
|
|
|
|
//
|
|
// If necessary, release the LSA Database lock.
|
|
//
|
|
|
|
if (ContainerReferenced) {
|
|
|
|
LsapDbReleaseLock();
|
|
}
|
|
|
|
#ifdef TRACK_HANDLE_CLOSE
|
|
if (*SecretHandle == LsapDbHandle)
|
|
{
|
|
DbgPrint("BUGBUG: Closing global policy handle\n");
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
return( Status );
|
|
|
|
CreateSecretError:
|
|
|
|
//
|
|
// If necessary, dereference the Container Object.
|
|
//
|
|
|
|
if (ContainerReferenced) {
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&PolicyHandle,
|
|
PolicyObject,
|
|
DereferenceOptions,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
|
|
ContainerReferenced = FALSE;
|
|
}
|
|
|
|
goto CreateSecretFinish;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsarOpenSecret(
|
|
IN LSAPR_HANDLE ConnectHandle,
|
|
IN PLSAPR_UNICODE_STRING SecretName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PLSAPR_HANDLE SecretHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the LSA server RPC worker routine for the LsaOpenSecret
|
|
API.
|
|
|
|
The LsaOpenSecret API opens a Secret Object within the LSA Database.
|
|
A handle is returned which must be used to perform operations on the
|
|
secret object.
|
|
|
|
Arguments:
|
|
|
|
ConnectHandle - Handle from an LsaOpenLsa call.
|
|
|
|
DesiredAccess - This is an access mask indicating accesses being
|
|
requested for the secret object being opened. These access types
|
|
are reconciled with the Discretionary Access Control List of the
|
|
target secret object to determine whether the accesses will be
|
|
granted or denied.
|
|
|
|
SecretName - Pointer to a Unicode String structure that references the
|
|
name of the Secret object to be opened.
|
|
|
|
SecretHandle - Pointer to location that will receive a handle to the
|
|
newly opened Secret object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_OBJECT_NAME_NOT_FOUND - There is no Secret object in the
|
|
target system's LSA Database having the specified SecretName.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_OBJECT_INFORMATION ObjectInformation;
|
|
BOOLEAN ContainerReferenced = FALSE;
|
|
BOOLEAN AcquiredLock = FALSE;
|
|
BOOLEAN GlobalSecret = FALSE;
|
|
ULONG OpenOptions = 0;
|
|
ULONG ReferenceOptions = LSAP_DB_ACQUIRE_LOCK;
|
|
|
|
//
|
|
// Check for Local Secret open request. If the Secret name does
|
|
// not begin with the Global Secret Prefix, the Secret is local. In
|
|
// this case, update/deletion of the secret is allowed on BDC's as well as
|
|
// PDC's and Workstations. Update/deletion of Global Secrets is not
|
|
// allowed on BDC's except for trusted callers such as a Replicator.
|
|
// To facilitate validation of the open request on BDC's we record
|
|
// that the BDC check should be omitted on the container reference, and
|
|
// that the replicator notification should be omitted on a commit.
|
|
//
|
|
|
|
Status = LsapDbGetScopeSecret( SecretName, &GlobalSecret );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenSecretError;
|
|
}
|
|
|
|
if (!GlobalSecret) {
|
|
|
|
OpenOptions |= (LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK |
|
|
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION );
|
|
}
|
|
|
|
//
|
|
// Acquire the Lsa Database lock. Verify that the connection handle
|
|
// (container object handle) is valid, and is of the expected type.
|
|
// Reference the container object handle. This reference remains in
|
|
// effect until the child object handle is closed.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
ConnectHandle,
|
|
0,
|
|
PolicyObject,
|
|
ReferenceOptions
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenSecretError;
|
|
}
|
|
|
|
AcquiredLock = TRUE;
|
|
ContainerReferenced =TRUE;
|
|
|
|
//
|
|
// Setup Object Information prior to calling the LSA Database Object
|
|
// Open routine. The Object Type, Container Object Type and
|
|
// Logical Name (derived from the Sid) need to be filled in.
|
|
//
|
|
|
|
ObjectInformation.ObjectTypeId = SecretObject;
|
|
ObjectInformation.ContainerTypeId = PolicyObject;
|
|
ObjectInformation.Sid = NULL;
|
|
|
|
//
|
|
// Initialize the Object Attributes. The Container Object Handle and
|
|
// Logical Name (Internal Name) of the object must be set up.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectInformation.ObjectAttributes,
|
|
(PUNICODE_STRING)SecretName,
|
|
0,
|
|
ConnectHandle,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the specific Secret object. Note that the account object
|
|
// handle returned is an RPC Context Handle.
|
|
//
|
|
|
|
Status = LsapDbOpenObject(
|
|
&ObjectInformation,
|
|
DesiredAccess,
|
|
OpenOptions,
|
|
SecretHandle
|
|
);
|
|
|
|
//
|
|
// If the open failed, dereference the container object.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenSecretError;
|
|
}
|
|
|
|
OpenSecretFinish:
|
|
|
|
//
|
|
// If necessary, release the LSA Database lock.
|
|
//
|
|
|
|
if (AcquiredLock) {
|
|
|
|
LsapDbReleaseLock();
|
|
}
|
|
|
|
#ifdef TRACK_HANDLE_CLOSE
|
|
if (*SecretHandle == LsapDbHandle)
|
|
{
|
|
DbgPrint("BUGBUG: Closing global policy handle\n");
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
return(Status);
|
|
|
|
OpenSecretError:
|
|
|
|
//
|
|
// If necessary, dereference the Container Object handle. Note that
|
|
// this is only done in the error case. In the non-error case, the
|
|
// Container handle stays referenced until the Account object is
|
|
// closed.
|
|
//
|
|
|
|
if (ContainerReferenced) {
|
|
|
|
*SecretHandle = NULL;
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&ConnectHandle,
|
|
PolicyObject,
|
|
0,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
}
|
|
|
|
goto OpenSecretFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsarSetSecret(
|
|
IN LSAPR_HANDLE SecretHandle,
|
|
IN OPTIONAL PLSAPR_CR_CIPHER_VALUE CipherCurrentValue,
|
|
IN OPTIONAL PLSAPR_CR_CIPHER_VALUE CipherOldValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the LSA server RPC worker routine for the LsaSetSecret
|
|
API.
|
|
|
|
The LsaSetSecret API optionally sets one or both values associated with
|
|
a Secret object. These values are known as the Current Value and
|
|
Old Value of the Secret object and these values have a meaning known to
|
|
the creator of the object.
|
|
|
|
This worker routine receives the Secret values in encrypted form from
|
|
the client. A two-way encryption algorithm using the Session Key will
|
|
havge been applied. The values received will first be decrypted using
|
|
this same key and then two-way encrypted using the LSA Database Private
|
|
Encryption Key. The resulting re-encrypted values will then be stored
|
|
as attributes of the Secret object.
|
|
|
|
Arguments:
|
|
|
|
SecretHandle - Handle from an LsaOpenSecret or LsaCreateSecret call.
|
|
|
|
CipherCurrentValue - Optional pointer to an encrypted value structure
|
|
containing the Current Value (if any) to be set for the Secret
|
|
Object (if any). This value is two-way encrypted with the Session
|
|
Key. If NULL is specified, the existing Current Value will be left
|
|
assigned to the object will be left unchanged.
|
|
|
|
CipherOldValue - Optional pointer to an encrypted value structure
|
|
containing the "old value" (if any) to be set for the Secret
|
|
Object (if any). If NULL is specified, the existing Old Value will be
|
|
assigned to the object will be left unchanged.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INVALID_HANDLE - Handle is invalid.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) SecretHandle;
|
|
|
|
PLSAP_CR_CLEAR_VALUE ClearCurrentValue = NULL;
|
|
PLSAP_CR_CLEAR_VALUE ClearOldValue = NULL;
|
|
PLSAP_CR_CIPHER_VALUE DbCipherCurrentValue = NULL;
|
|
ULONG DbCipherCurrentValueLength;
|
|
PLSAP_CR_CIPHER_VALUE DbCipherOldValue = NULL;
|
|
ULONG DbCipherOldValueLength;
|
|
PLSAP_CR_CIPHER_KEY SessionKey = NULL;
|
|
LARGE_INTEGER UpdatedTime;
|
|
BOOLEAN ObjectReferenced = FALSE;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) SecretHandle;
|
|
ULONG ReferenceOptions = LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION;
|
|
ULONG DereferenceOptions = LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION;
|
|
BOOLEAN GlobalSecret;
|
|
|
|
//
|
|
// Check for Local Secret set request. If the Secret name does
|
|
// not begin with the Global Secret Prefix, the Secret is local. In
|
|
// this case, update of the secret is allowed on BDC's as well as
|
|
// PDC's and Workstations. Creation of Global Secrets is not
|
|
// allowed on BDC's except for trusted callers such as a Replicator.
|
|
//
|
|
|
|
Status = LsapDbGetScopeSecret(
|
|
(PLSAPR_UNICODE_STRING) &InternalHandle->LogicalNameU,
|
|
&GlobalSecret
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
if (!GlobalSecret) {
|
|
|
|
ReferenceOptions |= LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK;
|
|
DereferenceOptions |= LSAP_DB_OMIT_REPLICATOR_NOTIFICATION;
|
|
}
|
|
|
|
//
|
|
// If the client is non-trusted, obtain the Session Key used by the
|
|
// client to two-way encrypt the Current Value and/or Old Values.
|
|
//
|
|
|
|
if (!InternalHandle->Trusted) {
|
|
|
|
Status = LsapCrServerGetSessionKey( SecretHandle, &SessionKey);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Acquire the Lsa Database lock. Verify that the Secret Object handle is
|
|
// valid, is of the expected type and has all of the desired accesses
|
|
// granted. Reference the handle and open a database transaction.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
SecretHandle,
|
|
SECRET_SET_VALUE,
|
|
SecretObject,
|
|
ReferenceOptions
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
ObjectReferenced = TRUE;
|
|
|
|
//
|
|
// If a Current Value is specified for the Secret Object, and the
|
|
// client is non-trusted, decrypt the value using the Session Key and
|
|
// encrypt it using the LSA Database System Key. Then (for all
|
|
// clients) encrypt the resulting value with the internal LSA Database
|
|
// encryption key and write resulting Value structure (header followed by
|
|
// buffer to the Policy Database as the Current Value attribute of the
|
|
// Secret object. If no Current Value is specified, or a NULL
|
|
// string is specified, the existing Current Value will be deleted.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(CipherCurrentValue)) {
|
|
|
|
if (!InternalHandle->Trusted) {
|
|
|
|
Status = LsapCrDecryptValue(
|
|
(PLSAP_CR_CIPHER_VALUE) CipherCurrentValue,
|
|
SessionKey,
|
|
&ClearCurrentValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
} else {
|
|
|
|
ClearCurrentValue = (PLSAP_CR_CLEAR_VALUE) CipherCurrentValue;
|
|
}
|
|
|
|
Status = LsapCrEncryptValue(
|
|
ClearCurrentValue,
|
|
LsapDbCipherKey,
|
|
&DbCipherCurrentValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
DbCipherCurrentValueLength = DbCipherCurrentValue->Length
|
|
+ (ULONG) sizeof(LSAP_CR_CIPHER_VALUE);
|
|
|
|
} else {
|
|
|
|
DbCipherCurrentValue = NULL;
|
|
DbCipherCurrentValueLength = 0;
|
|
}
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
SecretHandle,
|
|
&LsapDbNames[CurrVal],
|
|
DbCipherCurrentValue,
|
|
DbCipherCurrentValueLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
//
|
|
// Store the time at which the Current Secret value was last updated.
|
|
//
|
|
|
|
Status = NtQuerySystemTime(&UpdatedTime);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
SecretHandle,
|
|
&LsapDbNames[CupdTime],
|
|
&UpdatedTime,
|
|
sizeof (LARGE_INTEGER)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
//
|
|
// If an Old Value is specified for the Secret Object, and the
|
|
// client is non-trusted, decrypt the value using the Session Key and
|
|
// encrypt it using the LSA Database System Key. Then (for all
|
|
// clients) encrypt the resulting value with the internal LSA Database
|
|
// encryption key and write resulting Value structure (header followed by
|
|
// buffer to the Policy Database as the Old Value attribute of the
|
|
// Secret object. If no Old Value is specified, or a NULL
|
|
// string is specified, the existing Old Value will be deleted.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(CipherOldValue)) {
|
|
|
|
if (!InternalHandle->Trusted) {
|
|
|
|
Status = LsapCrDecryptValue(
|
|
(PLSAP_CR_CIPHER_VALUE) CipherOldValue,
|
|
SessionKey,
|
|
&ClearOldValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
} else {
|
|
|
|
ClearOldValue = (PLSAP_CR_CLEAR_VALUE) CipherOldValue;
|
|
}
|
|
|
|
Status = LsapCrEncryptValue(
|
|
ClearOldValue,
|
|
LsapDbCipherKey,
|
|
&DbCipherOldValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
DbCipherOldValueLength =
|
|
DbCipherOldValue->Length + (ULONG) sizeof(LSAP_CR_CIPHER_VALUE);
|
|
|
|
} else {
|
|
|
|
DbCipherOldValue = NULL;
|
|
DbCipherOldValueLength = 0;
|
|
}
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
SecretHandle,
|
|
&LsapDbNames[OldVal],
|
|
DbCipherOldValue,
|
|
DbCipherOldValueLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
//
|
|
// Store the time at which the Old Secret value was last updated.
|
|
//
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
SecretHandle,
|
|
&LsapDbNames[OupdTime],
|
|
&UpdatedTime,
|
|
sizeof (LARGE_INTEGER)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
SetSecretFinish:
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Session Key.
|
|
//
|
|
|
|
if (SessionKey != NULL) {
|
|
|
|
MIDL_user_free(SessionKey);
|
|
SessionKey = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for Decrypted Current Value.
|
|
// Note that for trusted clients, the decryption is the identity
|
|
// mapping, so do not do the free in this case.
|
|
//
|
|
|
|
if ((ClearCurrentValue != NULL) && !InternalHandle->Trusted) {
|
|
|
|
LsapCrFreeMemoryValue( ClearCurrentValue );
|
|
ClearCurrentValue = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for Decrypted Old Value.
|
|
// Note that for trusted clients, the decryption is the identity
|
|
// mapping, so do not do the free in this case.
|
|
//
|
|
|
|
if ((ClearOldValue != NULL) && !InternalHandle->Trusted) {
|
|
|
|
LsapCrFreeMemoryValue( ClearOldValue );
|
|
ClearOldValue = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Current Value
|
|
// encrypted for storage in the LSA Database.
|
|
//
|
|
|
|
if (DbCipherCurrentValue != NULL) {
|
|
|
|
LsapCrFreeMemoryValue( DbCipherCurrentValue );
|
|
DbCipherCurrentValue = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Old Value
|
|
// encrypted for storage in the LSA Database.
|
|
//
|
|
|
|
if (DbCipherOldValue != NULL) {
|
|
|
|
LsapCrFreeMemoryValue( DbCipherOldValue );
|
|
DbCipherOldValue = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, dereference the Secret object, close the database
|
|
// transaction, notify the LSA Database Replicator of the change,
|
|
// release the LSA Database lock and return.
|
|
//
|
|
|
|
if (ObjectReferenced) {
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&SecretHandle,
|
|
SecretObject,
|
|
DereferenceOptions,
|
|
SecurityDbChange,
|
|
Status
|
|
);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
SetSecretError:
|
|
|
|
goto SetSecretFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsarQuerySecret(
|
|
IN LSAPR_HANDLE SecretHandle,
|
|
IN OUT OPTIONAL PLSAPR_CR_CIPHER_VALUE *CipherCurrentValue,
|
|
OUT OPTIONAL PLARGE_INTEGER CurrentValueSetTime,
|
|
IN OUT OPTIONAL PLSAPR_CR_CIPHER_VALUE *CipherOldValue,
|
|
OUT OPTIONAL PLARGE_INTEGER OldValueSetTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the LSA server RPC worker routine for the LsaQuerySecret
|
|
API.
|
|
|
|
The LsaQuerySecret API optionally returns one or both of the values
|
|
assigned to a Secret object. These values are known as the "current value"
|
|
and the "old value", and they have a meaning known to the creator of the
|
|
Secret object. The values are returned in their encrypted form.
|
|
The caller must have LSA_QUERY_SECRET access to the Secret object.
|
|
|
|
Arguments:
|
|
|
|
SecretHandle - Handle from an LsaOpenSecret or LsaCreateSecret call.
|
|
|
|
CipherCurrentValue - Optional pointer to location which will receive a
|
|
pointer to an encrypted Unicode String structure containing the
|
|
"current value" of the Secret Object (if any) in encrypted form.
|
|
If no "current value" is assigned to the Secret object, a NULL pointer
|
|
is returned.
|
|
|
|
CurrentValueSetTime - The date/time at which the current secret value
|
|
was established.
|
|
|
|
CipherOldValue - Optional pointer to location which will receive a
|
|
pointer to an encrypted Unicode String structure containing the
|
|
"old value" of the Secret Object (if any) in encrypted form.
|
|
If no "old value" is assigned to the Secret object, a NULL pointer
|
|
is returned.
|
|
|
|
OldValueSetTime - The date/time at which the old secret value
|
|
was established.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) SecretHandle;
|
|
PLSAP_CR_CIPHER_VALUE OutputCipherCurrentValue = NULL;
|
|
PLSAP_CR_CIPHER_VALUE OutputCipherOldValue = NULL;
|
|
PLSAP_CR_CIPHER_KEY SessionKey = NULL;
|
|
BOOLEAN ObjectReferenced = FALSE;
|
|
ULONG ValueSetTimeLength;
|
|
|
|
//
|
|
// If the caller is non-trusted, obtain the Session Key used by the
|
|
// client to two-way encrypt the Current Value and/or Old Values.
|
|
// Trusted Clients do not use encryption since they are calling
|
|
// this server service directly and not via RPC.
|
|
//
|
|
|
|
if (!InternalHandle->Trusted) {
|
|
|
|
Status = LsapCrServerGetSessionKey( SecretHandle, &SessionKey );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecretError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Acquire the Lsa Database lock. Verify that the Secret Object handle is
|
|
// valid, is of the expected type and has all of the desired accesses
|
|
// granted. Reference the handle and open a database transaction.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
SecretHandle,
|
|
SECRET_QUERY_VALUE,
|
|
SecretObject,
|
|
LSAP_DB_ACQUIRE_LOCK
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecretError;
|
|
}
|
|
|
|
ObjectReferenced = TRUE;
|
|
|
|
//
|
|
// If requested, query the Current Value attribute of the Secret Object.
|
|
// For non-trusted callers, the Current value will be returned in
|
|
// encrypted form embedded within a structure.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(CipherCurrentValue)) {
|
|
|
|
Status = LsapDbQueryValueSecret(
|
|
SecretHandle,
|
|
&LsapDbNames[CurrVal],
|
|
SessionKey,
|
|
&OutputCipherCurrentValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecretError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If requested, query the Old Value attribute of the Secret Object.
|
|
// For non-trusted callers, the Old Value will be returned in
|
|
// encrypted form embedded within a structure.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(CipherOldValue)) {
|
|
|
|
Status = LsapDbQueryValueSecret(
|
|
SecretHandle,
|
|
&LsapDbNames[OldVal],
|
|
SessionKey,
|
|
&OutputCipherOldValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecretError;
|
|
}
|
|
}
|
|
|
|
ValueSetTimeLength = sizeof (LARGE_INTEGER);
|
|
|
|
//
|
|
// If requested, Query the time at which the Current Value of the Secret
|
|
// was last established. If the Current Value has never been set, return
|
|
// the time at which the Secret was created.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(CurrentValueSetTime)) {
|
|
|
|
Status = LsapDbReadAttributeObject(
|
|
SecretHandle,
|
|
&LsapDbNames[CupdTime],
|
|
CurrentValueSetTime,
|
|
&ValueSetTimeLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecretError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If requested, Query the time at which the Old Value of the Secret
|
|
// was last established. If the Old Value has never been set, return
|
|
// the time at which the Secret was created.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(OldValueSetTime)) {
|
|
|
|
Status = LsapDbReadAttributeObject(
|
|
SecretHandle,
|
|
&LsapDbNames[OupdTime],
|
|
OldValueSetTime,
|
|
&ValueSetTimeLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecretError;
|
|
}
|
|
}
|
|
|
|
QuerySecretFinish:
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Session Key.
|
|
//
|
|
|
|
if (SessionKey != NULL) {
|
|
|
|
MIDL_user_free(SessionKey);
|
|
}
|
|
|
|
//
|
|
// Return Current and/or Old Values of Secret Object, or NULL to
|
|
// caller. In error cases, NULL will be returned.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(CipherCurrentValue)) {
|
|
|
|
(PLSAP_CR_CIPHER_VALUE) *CipherCurrentValue = OutputCipherCurrentValue;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(CipherOldValue)) {
|
|
|
|
(PLSAP_CR_CIPHER_VALUE) *CipherOldValue = OutputCipherOldValue;
|
|
}
|
|
|
|
//
|
|
// If necessary, dereference the Secret object, close the database
|
|
// transaction, release the LSA Database lock and return.
|
|
//
|
|
|
|
if (ObjectReferenced) {
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&SecretHandle,
|
|
SecretObject,
|
|
LSAP_DB_RELEASE_LOCK,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
QuerySecretError:
|
|
|
|
goto QuerySecretFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbQueryValueSecret(
|
|
IN LSAPR_HANDLE SecretHandle,
|
|
IN PUNICODE_STRING ValueName,
|
|
IN OPTIONAL PLSAP_CR_CIPHER_KEY SessionKey,
|
|
OUT PLSAP_CR_CIPHER_VALUE *CipherValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function queries the specified value of a Secret Object. If
|
|
the caller is non-trusted, the value returned will have been two-way
|
|
encrypted with the Session Key. If the caller is trusted, no
|
|
encryption is done since the caller is calling us directly.
|
|
|
|
Arguments:
|
|
|
|
SecretHandle - Handle to Secret Object.
|
|
|
|
ValueName - Unicode name of the Secret Value to be queried. This
|
|
name is either "Currval" (for the Current Value) or "OldVal"
|
|
(for the Old Value.
|
|
|
|
SessionKey - Pointer to Session Key to be used for two-way encryption
|
|
of the value to be returned. This pointer must be non-NULL
|
|
except for Trusted Clients, where it must be NULL.
|
|
|
|
CipherValue - Receives 32-bit counted string pointer to Secret Value
|
|
queried. For non-trusted clients, the value will be encrypted.
|
|
|
|
WARNING - Note that CipherValue is defined to RPC as
|
|
"allocate(all_nodes)". This means that it is returned
|
|
in one contiguous block of memory rather than two, as
|
|
it would appear by the structure definition.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG DbCipherValueLength;
|
|
PLSAP_CR_CLEAR_VALUE ClearValue = NULL;
|
|
PLSAP_CR_CIPHER_VALUE DbCipherValue = NULL;
|
|
PLSAP_CR_CIPHER_VALUE OutputCipherValue = NULL;
|
|
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) SecretHandle;
|
|
|
|
//
|
|
// Get length of the specified Value attribute of the Secret object.
|
|
//
|
|
|
|
DbCipherValueLength = 0;
|
|
|
|
Status = LsapDbReadAttributeObject(
|
|
SecretHandle,
|
|
ValueName,
|
|
NULL,
|
|
&DbCipherValueLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
goto QueryValueSecretError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
*CipherValue = NULL;
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// We successfully read the length of the stored Secret Object value
|
|
// plus header from the Policy Database. Verify that the Secret
|
|
// Object value is either at least as long as a Cipher Value
|
|
// structure header, or is of length 0.
|
|
//
|
|
|
|
if (DbCipherValueLength < sizeof (LSAP_CR_CIPHER_VALUE)) {
|
|
|
|
if (DbCipherValueLength == 0) {
|
|
|
|
goto QueryValueSecretFinish;
|
|
}
|
|
|
|
Status = STATUS_INTERNAL_DB_ERROR;
|
|
goto QueryValueSecretError;
|
|
}
|
|
|
|
//
|
|
// Allocate memory for reading the specified Value of the Secret object.
|
|
// This value is stored in the Policy Database in the form of a
|
|
// Self-Relative Value structure. The Value Buffer part is encrypted.
|
|
//
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
DbCipherValue = MIDL_user_allocate(DbCipherValueLength);
|
|
|
|
if (DbCipherValue == NULL) {
|
|
|
|
goto QueryValueSecretError;
|
|
}
|
|
|
|
//
|
|
// Read the specified Policy-database-encrypted Value attribute.
|
|
//
|
|
|
|
Status = LsapDbReadAttributeObject(
|
|
SecretHandle,
|
|
ValueName,
|
|
DbCipherValue,
|
|
&DbCipherValueLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QueryValueSecretError;
|
|
}
|
|
|
|
//
|
|
// Verify that Lengths in returned header are consistent
|
|
// and also match returned data length - header size.
|
|
//
|
|
|
|
Status = STATUS_INTERNAL_DB_ERROR;
|
|
|
|
if (DbCipherValue->Length > DbCipherValue->MaximumLength) {
|
|
|
|
goto QueryValueSecretError;
|
|
}
|
|
|
|
if ((DbCipherValue->Length + (ULONG) sizeof(LSAP_CR_CIPHER_VALUE)) !=
|
|
DbCipherValueLength) {
|
|
|
|
goto QueryValueSecretError;
|
|
}
|
|
|
|
//
|
|
// If the size of the Value structure is less than its header size,
|
|
// something is wrong.
|
|
//
|
|
|
|
if (DbCipherValueLength < sizeof(LSAP_CR_CIPHER_VALUE)) {
|
|
|
|
goto QueryValueSecretError;
|
|
}
|
|
|
|
//
|
|
// If the string length is 0, something is wrong.
|
|
//
|
|
|
|
if (DbCipherValue->Length == 0) {
|
|
|
|
goto QueryValueSecretError;
|
|
}
|
|
|
|
//
|
|
// Store pointer to Value buffer in the Value structure. This pointer
|
|
// points just after the header. Then decrypt the Value using the
|
|
// LSA Database Cipher Key and encrypt the result using the Session Key.
|
|
//
|
|
|
|
DbCipherValue->Buffer = (PUCHAR)(DbCipherValue + 1);
|
|
|
|
Status = LsapCrDecryptValue(
|
|
DbCipherValue,
|
|
LsapDbCipherKey,
|
|
&ClearValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QueryValueSecretError;
|
|
}
|
|
|
|
//
|
|
// If the client is non-Trusted, encrypt the value with the Session
|
|
// Key, otherwise, leave it unchanged.
|
|
//
|
|
|
|
if (!InternalHandle->Trusted) {
|
|
|
|
Status = LsapCrEncryptValue(
|
|
ClearValue,
|
|
SessionKey,
|
|
&OutputCipherValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QueryValueSecretError;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Trusted clients get a clear-text block back.
|
|
// The block contains both the header and the text.
|
|
//
|
|
|
|
OutputCipherValue = (PLSAP_CR_CIPHER_VALUE)(ClearValue);
|
|
}
|
|
|
|
QueryValueSecretFinish:
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Db-encrypted Secret
|
|
// object Value read from the Policy Database.
|
|
//
|
|
|
|
if (DbCipherValue != NULL) {
|
|
|
|
LsapCrFreeMemoryValue( DbCipherValue );
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Decrypted Value.
|
|
// Trusted client's get a pointer to ClearValue back, so don't
|
|
// free it in this case.
|
|
//
|
|
|
|
if (!InternalHandle->Trusted && ClearValue != NULL) {
|
|
|
|
LsapCrFreeMemoryValue( ClearValue );
|
|
}
|
|
|
|
//
|
|
// Return pointer to Cipher Value (Clear Value for trusted clients) or
|
|
// NULL.
|
|
//
|
|
|
|
*CipherValue = OutputCipherValue;
|
|
return(Status);
|
|
|
|
QueryValueSecretError:
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Secret object value
|
|
// after re-encryption for return to the Client.
|
|
//
|
|
|
|
if (OutputCipherValue != NULL) {
|
|
|
|
LsapCrFreeMemoryValue( OutputCipherValue );
|
|
}
|
|
|
|
goto QueryValueSecretFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaIEnumerateSecrets(
|
|
IN LSAPR_HANDLE PolicyHandle,
|
|
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
|
OUT PVOID *Buffer,
|
|
IN ULONG PreferedMaximumLength,
|
|
OUT PULONG CountReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This service returns information about Secret objects. Since there
|
|
may be more information than can be returned in a single call of the
|
|
routine, multiple calls can be made to get all of the information.
|
|
To support this feature, the caller is provided with a handle that
|
|
can be used across calls to the API. On the initial call,
|
|
EnumerationContext should point to a variable that has been
|
|
initialized to 0.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Trusted handle to an open Policy Object.
|
|
|
|
EnumerationContext - Zero-based index at which to start the enumeration.
|
|
|
|
Buffer - Receives a pointer to a buffer containing information for
|
|
one or more Secret objects. This information is an array of
|
|
structures of type UNICODE_STRING, with each entry providing the
|
|
name of a single Secret object. When this information is no
|
|
longer needed, it must be released using MIDL_user_free.
|
|
|
|
PreferedMaximumLength - Prefered maximum length of the returned
|
|
data (in 8-bit bytes). This is not a hard upper limit but
|
|
serves as a guide. Due to data conversion between systems
|
|
with different natural data sizes, the actual amount of data
|
|
returned may be greater than this value.
|
|
|
|
CountReturned - Numer of entries returned.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_NO_MORE_ENTRIES - No entries have been returned because
|
|
there are no more.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_NAME_ENUMERATION_BUFFER DbEnumerationBuffer;
|
|
PUNICODE_STRING SecretNames = NULL;
|
|
PUNICODE_STRING NextSecretName = NULL;
|
|
PSID *Sids = NULL;
|
|
LSAP_DB_ATTRIBUTE DomainNameAttribute;
|
|
LSAPR_HANDLE SecretHandle = NULL;
|
|
ULONG MaxLength;;
|
|
|
|
DomainNameAttribute.AttributeValue = NULL;
|
|
|
|
//
|
|
// If no Enumeration Structure is provided, return an error.
|
|
//
|
|
|
|
|
|
if ( !ARGUMENT_PRESENT(Buffer) ||
|
|
!ARGUMENT_PRESENT(EnumerationContext) ) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Initialize the internal Lsa Database Enumeration Buffer, and
|
|
// the provided Enumeration Buffer to NULL.
|
|
//
|
|
|
|
DbEnumerationBuffer.EntriesRead = 0;
|
|
DbEnumerationBuffer.Names = NULL;
|
|
*Buffer = NULL;
|
|
DomainNameAttribute.AttributeValue = NULL;
|
|
|
|
//
|
|
// Acquire the Lsa Database lock. Verify that the connection handle is
|
|
// valid, is of the expected type and has all of the desired accesses
|
|
// granted. Reference the handle.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
PolicyHandle,
|
|
POLICY_VIEW_LOCAL_INFORMATION,
|
|
PolicyObject,
|
|
LSAP_DB_ACQUIRE_LOCK
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Limit the enumeration length except for trusted callers
|
|
//
|
|
|
|
if ( !((LSAP_DB_HANDLE) PolicyHandle)->Trusted &&
|
|
(PreferedMaximumLength > LSA_MAXIMUM_ENUMERATION_LENGTH)
|
|
) {
|
|
MaxLength = LSA_MAXIMUM_ENUMERATION_LENGTH;
|
|
} else {
|
|
MaxLength = PreferedMaximumLength;
|
|
}
|
|
|
|
//
|
|
// Call general enumeration routine. This will return an array
|
|
// of names of secrets.
|
|
//
|
|
|
|
Status = LsapDbEnumerateNames(
|
|
PolicyHandle,
|
|
SecretObject,
|
|
EnumerationContext,
|
|
&DbEnumerationBuffer,
|
|
MaxLength
|
|
);
|
|
|
|
//
|
|
// At this point:
|
|
//
|
|
// SUCCESS -> Some names are being returned (may or
|
|
// may not be additional names to be retrieved
|
|
// in future calls).
|
|
//
|
|
// NO_MORE_ENTRIES -> There are NO names to return
|
|
// for this or any future call.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Return the number of entries read. Note that the Enumeration Buffer
|
|
// returned from LsapDbEnumerateNames is expected to be non-null
|
|
// in all non-error cases.
|
|
//
|
|
|
|
ASSERT(DbEnumerationBuffer.EntriesRead != 0);
|
|
|
|
|
|
//
|
|
// Now copy the output array of Unicode Strings for the caller.
|
|
// Memory for the array and the Unicode Buffers is allocated via
|
|
// MIDL_user_allocate.
|
|
//
|
|
|
|
Status = LsapRpcCopyUnicodeStrings(
|
|
NULL,
|
|
DbEnumerationBuffer.EntriesRead,
|
|
&SecretNames,
|
|
DbEnumerationBuffer.Names
|
|
);
|
|
}
|
|
|
|
//
|
|
// Dereference retains current status value unless
|
|
// error occurs.
|
|
//
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&PolicyHandle,
|
|
PolicyObject,
|
|
LSAP_DB_RELEASE_LOCK,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
}
|
|
|
|
//
|
|
// Fill in returned Enumeration Structure, returning 0 or NULL for
|
|
// fields in the error case.
|
|
//
|
|
|
|
*Buffer = SecretNames;
|
|
*CountReturned = DbEnumerationBuffer.EntriesRead;
|
|
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaISetTimesSecret(
|
|
IN LSAPR_HANDLE SecretHandle,
|
|
IN PLARGE_INTEGER CurrentValueSetTime,
|
|
IN PLARGE_INTEGER OldValueSetTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This service is used to set the times associated with a Secret object.
|
|
This allows the times of secrets to be set to what they are on the
|
|
Primary Domain Controller (PDC) involved in an LSA Database replication
|
|
rather than being set to the time at which the Secret object is
|
|
created on a Backup Domain Controller (BDC) being replicated to.
|
|
|
|
Arguments:
|
|
|
|
SecretHandle - Trusted Handle to an open secret object. This will
|
|
have been obtained via a call to LsaCreateSecret() or LsaOpenSecret()
|
|
on which a Trusted Policy Handle was specified.
|
|
|
|
CurrentValueSetTime - The date and time to set for the date and time
|
|
at which the Current Value of the Secret object was set.
|
|
|
|
OldValueSetTime - The date and time to set for the date and time
|
|
at which the Old Value of the Secret object was set.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_ACCESS_DENIED - The supplied SecretHandle is not Trusted.
|
|
|
|
STATUS_INVALID_HANDLE - The supplied SecretHandle is not
|
|
a valid habdle to a Secret Object.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
|
|
LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) SecretHandle;
|
|
BOOLEAN ObjectReferenced = FALSE;
|
|
|
|
//
|
|
// Verify that both Times are specified.
|
|
//
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
if (!ARGUMENT_PRESENT( CurrentValueSetTime )) {
|
|
|
|
goto SetTimesSecretError;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT( CurrentValueSetTime )) {
|
|
|
|
goto SetTimesSecretError;
|
|
}
|
|
|
|
//
|
|
// Acquire the Lsa Database lock. Verify that the Secret Object handle is
|
|
// valid, is of the expected type and has all of the desired accesses
|
|
// granted. Reference the handle and open a database transaction.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
SecretHandle,
|
|
SECRET_SET_VALUE,
|
|
SecretObject,
|
|
LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION | LSAP_DB_TRUSTED
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetTimesSecretError;
|
|
}
|
|
|
|
ObjectReferenced = TRUE;
|
|
|
|
//
|
|
// Set the time at which the Current Secret value was last updated
|
|
// to the specified value.
|
|
//
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
SecretHandle,
|
|
&LsapDbNames[CupdTime],
|
|
CurrentValueSetTime,
|
|
sizeof (LARGE_INTEGER)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetTimesSecretError;
|
|
}
|
|
|
|
//
|
|
// Set the time at which the Old Secret value was last updated
|
|
// to the specified value.
|
|
//
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
SecretHandle,
|
|
&LsapDbNames[OupdTime],
|
|
OldValueSetTime,
|
|
sizeof (LARGE_INTEGER)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetTimesSecretError;
|
|
}
|
|
|
|
SetTimesSecretFinish:
|
|
|
|
//
|
|
// If necessary, dereference the Secret object, close the database
|
|
// transaction, notify the LSA Database Replicator of the change and
|
|
// release the LSA Database lock and return.
|
|
//
|
|
|
|
if (ObjectReferenced) {
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&SecretHandle,
|
|
SecretObject,
|
|
LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
|
|
SecurityDbChange,
|
|
Status
|
|
);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
SetTimesSecretError:
|
|
|
|
goto SetTimesSecretFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbGetScopeSecret(
|
|
IN PLSAPR_UNICODE_STRING SecretName,
|
|
OUT PBOOLEAN GlobalSecret
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function checks the scope of a Secret name. Secrets have either
|
|
Global or Local Scope.
|
|
|
|
Global Secrets are Secrets that are normally present on all DC's for a
|
|
Domain. They are replicated from PDC's to BDC's. On BDC's, only a
|
|
Trusted Client such as a replicator can create, update or delete Global
|
|
Secrets. Global Secrets are identified as Secrets whose name begins
|
|
with a designated prefix.
|
|
|
|
Local Secrets are Secrets that are private to a specific machine. They
|
|
are not replicated. Normal non-trusted clients may create, update or
|
|
delete Local Secrets. Local Secrets are idientified as Secrets whose
|
|
name does not begin with a designated prefix.
|
|
|
|
Arguments:
|
|
|
|
SecretName - Pointer to Unicode String containing the name of the
|
|
Secret to be checked.
|
|
|
|
GlobalSecret - Receives a Boolean indicating
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The Secret name is valid
|
|
|
|
STATUS_INVALID_PARAMETER - The Secret Name is invalid in such a
|
|
way as to prevent scope determination.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING GlobalPrefix;
|
|
BOOLEAN OutputGlobalSecret = FALSE;
|
|
|
|
//
|
|
// Initialize a Unicode String with the Global Secret name Prefix.
|
|
//
|
|
|
|
RtlInitUnicodeString( &GlobalPrefix, LSA_GLOBAL_SECRET_PREFIX );
|
|
|
|
//
|
|
// Now check if the given Name has the Global Prefix.
|
|
//
|
|
|
|
if (RtlPrefixUnicodeString( &GlobalPrefix, (PUNICODE_STRING) SecretName, TRUE)) {
|
|
|
|
OutputGlobalSecret = TRUE;
|
|
}
|
|
|
|
*GlobalSecret = OutputGlobalSecret;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbBuildSecretCache(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function builds a cache of Secret Objects. Currently, it is not
|
|
implemented
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
return(Status);
|
|
}
|
|
|