2770 lines
66 KiB
C
2770 lines
66 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
lsawrap.c
|
||
|
||
Abstract:
|
||
|
||
LSA - Database - wrapper APIs for secret, trusted domain, and account
|
||
objects.
|
||
|
||
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:
|
||
|
||
Mike Swift (MikeSw) December 12, 1994
|
||
|
||
Environment:
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "lsasrvp.h"
|
||
#include "dbp.h"
|
||
#include <lmcons.h> // required by logonmsv.h
|
||
#include <logonmsv.h> // SSI_SECRET_PREFIX...
|
||
|
||
//
|
||
// This structure holds the user right to system access mapping
|
||
//
|
||
|
||
typedef struct _LSAP_DB_RIGHT_AND_ACCESS {
|
||
UNICODE_STRING UserRight;
|
||
ULONG SystemAccess;
|
||
} LSAP_DB_RIGHT_AND_ACCESS, *PLSAP_DB_RIGHT_AND_ACCESS;
|
||
|
||
#define LSAP_DB_SYSTEM_ACCESS_TYPES 4
|
||
|
||
LSAP_DB_RIGHT_AND_ACCESS LsapDbRightAndAccess[LSAP_DB_SYSTEM_ACCESS_TYPES];
|
||
|
||
PSECURITY_DESCRIPTOR UserRightSD;
|
||
|
||
UNICODE_STRING UserRightTypeName;
|
||
|
||
GENERIC_MAPPING UserRightGenericMapping;
|
||
|
||
|
||
|
||
NTSTATUS
|
||
LsapDbInitializeRights(
|
||
)
|
||
{
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initializes global data for the new APIs handling user rights
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
STATUS_INSUFFICIENT_MEMORY - not enough memory to initialize the
|
||
data structures.
|
||
|
||
--*/
|
||
|
||
SECURITY_DESCRIPTOR AbsoluteDescriptor;
|
||
ULONG DaclLength;
|
||
NTSTATUS Status;
|
||
PACL Dacl = NULL;
|
||
HANDLE LsaProcessTokenHandle = NULL;
|
||
|
||
//
|
||
// Interactive logons
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&LsapDbRightAndAccess[0].UserRight,
|
||
SE_INTERACTIVE_LOGON_NAME
|
||
);
|
||
LsapDbRightAndAccess[0].SystemAccess = SECURITY_ACCESS_INTERACTIVE_LOGON;
|
||
|
||
//
|
||
// network logons
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&LsapDbRightAndAccess[1].UserRight,
|
||
SE_NETWORK_LOGON_NAME
|
||
);
|
||
LsapDbRightAndAccess[1].SystemAccess = SECURITY_ACCESS_NETWORK_LOGON;
|
||
|
||
//
|
||
// SERVICE logons
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&LsapDbRightAndAccess[2].UserRight,
|
||
SE_SERVICE_LOGON_NAME
|
||
);
|
||
LsapDbRightAndAccess[2].SystemAccess = SECURITY_ACCESS_SERVICE_LOGON;
|
||
|
||
//
|
||
// BATCH logons
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&LsapDbRightAndAccess[3].UserRight,
|
||
SE_BATCH_LOGON_NAME
|
||
);
|
||
LsapDbRightAndAccess[3].SystemAccess = SECURITY_ACCESS_BATCH_LOGON;
|
||
|
||
//
|
||
// Create the security descriptor for the rights pseudo-object
|
||
//
|
||
|
||
//
|
||
// The ACL looks like this:
|
||
//
|
||
// Admins - PRIVILEGE_VIEW | PRIVILEGE_ADJUST
|
||
//
|
||
|
||
Status = RtlCreateSecurityDescriptor(
|
||
&AbsoluteDescriptor,
|
||
SECURITY_DESCRIPTOR_REVISION
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
DaclLength = sizeof(ACL) -
|
||
sizeof(ULONG) + // for dummy in structure
|
||
sizeof(ACCESS_ALLOWED_ACE) +
|
||
RtlLengthSid(LsapAliasAdminsSid);
|
||
|
||
Dacl = (PACL) LsapAllocateLsaHeap(DaclLength);
|
||
if (Dacl == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = RtlCreateAcl(
|
||
Dacl,
|
||
DaclLength,
|
||
ACL_REVISION
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Now add the access allowed ace for Admins. They are granted
|
||
// PRIVILEGE_VIEW and PRIVILEGE_ADJUST access. For now,
|
||
// PRIVILEGE_ADJUST is unused (since you can't add accounts to a
|
||
// privilege).
|
||
//
|
||
// ********* NOTE *************
|
||
//
|
||
// If real privilege objects are ever implemented, this should be moved
|
||
// to dbinit.c where the other LSA objects are created, and added to
|
||
// the table of real LSA objects.
|
||
//
|
||
|
||
Status = RtlAddAccessAllowedAce(
|
||
Dacl,
|
||
ACL_REVISION,
|
||
PRIVILEGE_VIEW | PRIVILEGE_ADJUST,
|
||
LsapAliasAdminsSid
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = RtlSetOwnerSecurityDescriptor(
|
||
&AbsoluteDescriptor,
|
||
LsapAliasAdminsSid,
|
||
FALSE // owner not defaulted
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = RtlSetDaclSecurityDescriptor(
|
||
&AbsoluteDescriptor,
|
||
TRUE, // DACL present
|
||
Dacl,
|
||
FALSE // DACL defaulted
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
UserRightGenericMapping.GenericRead = PRIVILEGE_VIEW | STANDARD_RIGHTS_READ;
|
||
UserRightGenericMapping.GenericWrite = PRIVILEGE_ADJUST | STANDARD_RIGHTS_WRITE;
|
||
UserRightGenericMapping.GenericExecute = STANDARD_RIGHTS_EXECUTE;
|
||
UserRightGenericMapping.GenericAll = PRIVILEGE_ALL;
|
||
|
||
//
|
||
// Now open the Lsa process's token with appropriate access (token is
|
||
// needed to create the security object).
|
||
//
|
||
|
||
Status = NtOpenProcessToken(
|
||
NtCurrentProcess(),
|
||
TOKEN_QUERY,
|
||
&LsaProcessTokenHandle
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = RtlNewSecurityObject(
|
||
NULL,
|
||
&AbsoluteDescriptor,
|
||
&UserRightSD,
|
||
FALSE, // not directory object
|
||
LsaProcessTokenHandle,
|
||
&UserRightGenericMapping
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlInitUnicodeString(
|
||
&UserRightTypeName,
|
||
L"UserRightObject"
|
||
);
|
||
|
||
Cleanup:
|
||
if (Dacl != NULL) {
|
||
LsapFreeLsaHeap(Dacl);
|
||
}
|
||
if (LsaProcessTokenHandle != NULL) {
|
||
NtClose(LsaProcessTokenHandle);
|
||
}
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
LsapDbFindNextSidWithRight(
|
||
IN LSAPR_HANDLE ContainerHandle,
|
||
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
||
IN OPTIONAL PLUID Privilege,
|
||
IN OPTIONAL PULONG SystemAccess,
|
||
OUT PLSAPR_SID *NextSid
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function finds the next Sid of object of a given type within a
|
||
container object. The given object type must be one where objects
|
||
have Sids. The Sids returned can be used on subsequent open calls to
|
||
access the objects. The Account
|
||
|
||
Arguments:
|
||
|
||
ContainerHandle - Handle to container object.
|
||
|
||
EnumerationContext - Pointer to a variable containing the index of
|
||
the object to be found. A zero value indicates that the first
|
||
object is to be found.
|
||
|
||
Privilege - If present, determines what privilge the account must have.
|
||
|
||
SystemAccess - If present, determines what kind of system access the
|
||
account must have.
|
||
|
||
NextSid - Receives a pointer to the next Sid found.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Standard Nt Result Code
|
||
|
||
STATUS_INVALID_HANDLE - Invalid ContainerHandle specified
|
||
|
||
STATUS_NO_MORE_ENTRIES - Warning that no more entries exist.
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status, SecondaryStatus;
|
||
ULONG SidKeyValueLength = 0;
|
||
ULONG RightKeyValueLength = 0;
|
||
UNICODE_STRING SubKeyNameU;
|
||
UNICODE_STRING SidKeyNameU;
|
||
UNICODE_STRING RightKeyNameU;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
HANDLE ContDirKeyHandle = NULL;
|
||
HANDLE SidKeyHandle = NULL;
|
||
HANDLE RightKeyHandle = NULL;
|
||
PSID ObjectSid = NULL;
|
||
PPRIVILEGE_SET ObjectPrivileges = NULL;
|
||
PULONG ObjectAccess = NULL;
|
||
PBYTE ObjectRights = NULL;
|
||
ULONG Index;
|
||
BOOLEAN ValidSid = FALSE;
|
||
|
||
//
|
||
// Zero pointers for cleanup routine
|
||
//
|
||
|
||
SidKeyNameU.Buffer = NULL;
|
||
RightKeyNameU.Buffer = NULL;
|
||
|
||
//
|
||
// Setup object attributes for opening the appropriate Containing
|
||
// Directory. Since we're looking for Account objects,
|
||
// the containing Directory is "Accounts". The Unicode strings for
|
||
// containing Directories are set up during Lsa Initialization.
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&LsapDbContDirs[AccountObject],
|
||
OBJ_CASE_INSENSITIVE,
|
||
((LSAP_DB_HANDLE) ContainerHandle)->KeyHandle,
|
||
NULL
|
||
);
|
||
|
||
Status = RtlpNtOpenKey(
|
||
&ContDirKeyHandle,
|
||
KEY_READ,
|
||
&ObjectAttributes,
|
||
0
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
ContDirKeyHandle = NULL; // For error processing
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Initialize the Unicode String in which the next object's Logical Name
|
||
// will be returned. The Logical Name of an object equals its Registry
|
||
// Key relative to its Containing Directory, and is also equal to
|
||
// the Relative Id of the object represented in character form as an
|
||
// 8-digit number with leading zeros.
|
||
//
|
||
// NOTE: The size of buffer allocated for the Logical Name must be
|
||
// calculated dynamically when the Registry supports long names, because
|
||
// it is possible that the Logical Name of an object will be equal to a
|
||
// character representation of the full Sid, not just the Relative Id
|
||
// part.
|
||
//
|
||
|
||
SubKeyNameU.MaximumLength = (USHORT) LSAP_DB_LOGICAL_NAME_MAX_LENGTH;
|
||
SubKeyNameU.Length = 0;
|
||
SubKeyNameU.Buffer = LsapAllocateLsaHeap(SubKeyNameU.MaximumLength);
|
||
|
||
if (SubKeyNameU.Buffer == NULL) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Now enumerate the next subkey.
|
||
//
|
||
|
||
Status = RtlpNtEnumerateSubKey(
|
||
ContDirKeyHandle,
|
||
&SubKeyNameU,
|
||
*EnumerationContext,
|
||
NULL
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// If a right was passed in, check for that right
|
||
//
|
||
|
||
if ((Privilege != NULL) || (SystemAccess != NULL)){
|
||
|
||
ASSERT(((Privilege == NULL) && (SystemAccess != NULL)) ||
|
||
((SystemAccess == NULL) && (Privilege != NULL)));
|
||
|
||
//
|
||
// Construct a path to the Privilgs attribute of the object relative to
|
||
// the containing directory. This path has the form
|
||
//
|
||
// <Object Logical Name>"\Privilgs"
|
||
//
|
||
// The Logical Name of the object has just been returned by the
|
||
// above call to RtlpNtEnumerateSubKey.
|
||
//
|
||
|
||
if (Privilege != NULL) {
|
||
Status = LsapDbJoinSubPaths(
|
||
&SubKeyNameU,
|
||
&LsapDbNames[Privilgs],
|
||
&RightKeyNameU
|
||
);
|
||
} else {
|
||
Status = LsapDbJoinSubPaths(
|
||
&SubKeyNameU,
|
||
&LsapDbNames[ActSysAc],
|
||
&RightKeyNameU
|
||
);
|
||
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Setup object attributes for opening the privilege or access attribute
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&RightKeyNameU,
|
||
OBJ_CASE_INSENSITIVE,
|
||
ContDirKeyHandle,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Open the Sid attribute
|
||
//
|
||
|
||
Status = RtlpNtOpenKey(
|
||
&RightKeyHandle,
|
||
KEY_READ,
|
||
&ObjectAttributes,
|
||
0
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
Status = STATUS_NOT_ALL_ASSIGNED;
|
||
(*EnumerationContext)++;
|
||
}
|
||
|
||
SidKeyHandle = NULL;
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Now query the size of the buffer required to read the Sid
|
||
// attribute's value.
|
||
//
|
||
|
||
RightKeyValueLength = 0;
|
||
|
||
Status = RtlpNtQueryValueKey(
|
||
RightKeyHandle,
|
||
NULL,
|
||
NULL,
|
||
&RightKeyValueLength,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// We expect buffer overflow to be returned from a query buffer size
|
||
// call.
|
||
//
|
||
|
||
if (Status == STATUS_BUFFER_OVERFLOW) {
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Allocate memory for reading the Privileges attribute.
|
||
//
|
||
|
||
ObjectRights = MIDL_user_allocate(RightKeyValueLength);
|
||
|
||
if (ObjectRights == NULL) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Supplied buffer is large enough to hold the SubKey's value.
|
||
// Query the value.
|
||
//
|
||
|
||
Status = RtlpNtQueryValueKey(
|
||
RightKeyHandle,
|
||
NULL,
|
||
ObjectRights,
|
||
&RightKeyValueLength,
|
||
NULL
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Check for the system access or privilege specified
|
||
//
|
||
|
||
if (Privilege != NULL) {
|
||
|
||
ObjectPrivileges = (PPRIVILEGE_SET) ObjectRights;
|
||
|
||
for (Index = 0; Index < ObjectPrivileges->PrivilegeCount ; Index++) {
|
||
if (RtlEqualLuid(&ObjectPrivileges->Privilege[Index].Luid, Privilege)) {
|
||
ValidSid = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
} else if (SystemAccess != NULL) {
|
||
ObjectAccess = (PULONG) ObjectRights;
|
||
|
||
if (((*ObjectAccess) & (*SystemAccess)) != 0) {
|
||
ValidSid = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this sid didn't meet the criteria, return now. Make sure
|
||
// to bump up the context so we don't try this sid again.
|
||
//
|
||
|
||
if (!ValidSid) {
|
||
Status = STATUS_NOT_ALL_ASSIGNED;
|
||
(*EnumerationContext)++;
|
||
goto FindNextFinish;
|
||
}
|
||
|
||
} // privilege != NULL || systemaccess != NULL
|
||
|
||
//
|
||
// Construct a path to the Sid attribute of the object relative to
|
||
// the containing directory. This path has the form
|
||
//
|
||
// <Object Logical Name>"\Sid"
|
||
//
|
||
// The Logical Name of the object has just been returned by the
|
||
// above call to RtlpNtEnumerateSubKey.
|
||
//
|
||
|
||
Status = LsapDbJoinSubPaths(
|
||
&SubKeyNameU,
|
||
&LsapDbNames[Sid],
|
||
&SidKeyNameU
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Setup object attributes for opening the Sid attribute
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&SidKeyNameU,
|
||
OBJ_CASE_INSENSITIVE,
|
||
ContDirKeyHandle,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Open the Sid attribute
|
||
//
|
||
|
||
Status = RtlpNtOpenKey(
|
||
&SidKeyHandle,
|
||
KEY_READ,
|
||
&ObjectAttributes,
|
||
0
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
SidKeyHandle = NULL;
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Now query the size of the buffer required to read the Sid
|
||
// attribute's value.
|
||
//
|
||
|
||
SidKeyValueLength = 0;
|
||
|
||
Status = RtlpNtQueryValueKey(
|
||
SidKeyHandle,
|
||
NULL,
|
||
NULL,
|
||
&SidKeyValueLength,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// We expect buffer overflow to be returned from a query buffer size
|
||
// call.
|
||
//
|
||
|
||
if (Status == STATUS_BUFFER_OVERFLOW) {
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Allocate memory for reading the Sid attribute.
|
||
//
|
||
|
||
ObjectSid = MIDL_user_allocate(SidKeyValueLength);
|
||
|
||
if (ObjectSid == NULL) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto FindNextError;
|
||
}
|
||
|
||
//
|
||
// Supplied buffer is large enough to hold the SubKey's value.
|
||
// Query the value.
|
||
//
|
||
|
||
Status = RtlpNtQueryValueKey(
|
||
SidKeyHandle,
|
||
NULL,
|
||
ObjectSid,
|
||
&SidKeyValueLength,
|
||
NULL
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
goto FindNextError;
|
||
}
|
||
|
||
(*EnumerationContext)++;
|
||
|
||
//
|
||
// Return the Sid.
|
||
//
|
||
|
||
*NextSid = ObjectSid;
|
||
|
||
FindNextFinish:
|
||
|
||
//
|
||
// Cleanup the rights check
|
||
//
|
||
|
||
if (RightKeyHandle != NULL) {
|
||
|
||
SecondaryStatus = NtClose(RightKeyHandle);
|
||
|
||
#if DBG
|
||
|
||
if (!NT_SUCCESS(SecondaryStatus)) {
|
||
|
||
DbgPrint("LsapDbFindNextSid: NtClose failed 0x%lx\n", Status);
|
||
}
|
||
|
||
#endif // DBG
|
||
|
||
|
||
}
|
||
|
||
if (ObjectRights != NULL) {
|
||
MIDL_user_free(ObjectRights);
|
||
}
|
||
|
||
//
|
||
// If necessary, close the Sid key handle
|
||
//
|
||
|
||
if (SidKeyHandle != NULL) {
|
||
|
||
SecondaryStatus = NtClose(SidKeyHandle);
|
||
|
||
#if DBG
|
||
|
||
if (!NT_SUCCESS(SecondaryStatus)) {
|
||
|
||
DbgPrint("LsapDbFindNextSid: NtClose failed 0x%lx\n", Status);
|
||
}
|
||
|
||
#endif // DBG
|
||
|
||
}
|
||
|
||
//
|
||
// If necessary, close the containing directory handle
|
||
//
|
||
|
||
if (ContDirKeyHandle != NULL) {
|
||
|
||
SecondaryStatus = NtClose(ContDirKeyHandle);
|
||
|
||
#if DBG
|
||
if (!NT_SUCCESS(SecondaryStatus)) {
|
||
|
||
DbgPrint(
|
||
"LsapDbFindNextSid: NtClose failed 0x%lx\n",
|
||
Status
|
||
);
|
||
}
|
||
|
||
#endif // DBG
|
||
|
||
}
|
||
|
||
//
|
||
// If necessary, free the Unicode String buffer allocated by
|
||
// LsapDbJoinSubPaths for the Registry key name of the Sid attribute
|
||
// relative to the containing directory Registry key.
|
||
//
|
||
|
||
if (SidKeyNameU.Buffer != NULL) {
|
||
|
||
RtlFreeUnicodeString( &SidKeyNameU );
|
||
}
|
||
|
||
//
|
||
// If necessary, free the Unicode String buffer allocated for
|
||
// Registry key name of the object relative to its containing
|
||
// directory.
|
||
//
|
||
|
||
if (SubKeyNameU.Buffer != NULL) {
|
||
|
||
LsapFreeLsaHeap( SubKeyNameU.Buffer );
|
||
}
|
||
|
||
return(Status);
|
||
|
||
FindNextError:
|
||
|
||
//
|
||
// If necessary, free the memory allocated for the object's Sid.
|
||
//
|
||
|
||
if (ObjectSid != NULL) {
|
||
|
||
MIDL_user_free(ObjectSid);
|
||
*NextSid = NULL;
|
||
}
|
||
|
||
goto FindNextFinish;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
LsapDbEnumerateSidsWithRight(
|
||
IN LSAPR_HANDLE ContainerHandle,
|
||
IN OPTIONAL PLUID Privilege,
|
||
IN OPTIONAL PULONG SystemAccess,
|
||
OUT PLSAP_DB_SID_ENUMERATION_BUFFER DbEnumerationBuffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function enumerates Sids of objects of a given type within a container
|
||
object. 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. On the initial call,
|
||
EnumerationContext should point to a variable that has been initialized
|
||
to 0.
|
||
|
||
Arguments:
|
||
|
||
ContainerHandle - Handle to a container object.
|
||
|
||
Privilege - If present, specifies what privilege the account must have.
|
||
|
||
SystemAccess - If present, specifies what access type the account must
|
||
have. This cannot be present with Privilege.
|
||
|
||
EnumerationContext - API-specific handle to allow multiple calls
|
||
(see Routine Description above).
|
||
|
||
DbEnumerationBuffer - Receives a pointer to a structure that will receive
|
||
the count of entries returned in an enumeration information array, and
|
||
a pointer to the array. Currently, the only information returned is
|
||
the object Sids. These Sids may be used together with object type to
|
||
open the objects and obtain any further information available.
|
||
|
||
|
||
CountReturned - Pointer to variable which will receive a count of the
|
||
entries returned.
|
||
|
||
Return Values:
|
||
|
||
NTSTATUS - Standard Nt Result Code
|
||
|
||
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
||
to complete the operation.
|
||
|
||
STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
|
||
is returned if no objects have been enumerated because the
|
||
EnumerationContext value passed in is too high.
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
LSAP_DB_ENUMERATION_ELEMENT LastElement;
|
||
PLSAP_DB_ENUMERATION_ELEMENT FirstElement, NextElement = NULL, FreeElement;
|
||
PSID *Sids = NULL;
|
||
BOOLEAN PreferedMaximumReached = FALSE;
|
||
ULONG EntriesRead;
|
||
ULONG Index, EnumerationIndex;
|
||
BOOLEAN TrustedClient = ((LSAP_DB_HANDLE) ContainerHandle)->Trusted;
|
||
|
||
LastElement.Next = NULL;
|
||
FirstElement = &LastElement;
|
||
|
||
//
|
||
// If no enumeration buffer provided, return an error.
|
||
//
|
||
|
||
|
||
if ( !ARGUMENT_PRESENT(DbEnumerationBuffer) ) {
|
||
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
|
||
//
|
||
// Enumerate objects, stopping when the length of data to be returned
|
||
// reaches or exceeds the Prefered Maximum Length, or reaches the
|
||
// absolute maximum allowed for LSA object enumerations. We allow
|
||
// the last object enumerated to bring the total amount of data to
|
||
// be returned beyond the Prefered Maximum Length, but not beyond the
|
||
// absolute maximum length.
|
||
//
|
||
|
||
EnumerationIndex = 0;
|
||
|
||
for (EntriesRead = 0;;) {
|
||
|
||
//
|
||
// Allocate memory for next enumeration element (if we haven't
|
||
// already). Set the Sid field to NULL for cleanup purposes.
|
||
//
|
||
|
||
if (NextElement == NULL ) {
|
||
NextElement = MIDL_user_allocate(sizeof (LSAP_DB_ENUMERATION_ELEMENT));
|
||
|
||
if (NextElement == NULL) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
break;
|
||
}
|
||
}
|
||
|
||
NextElement->Sid = NULL;
|
||
|
||
//
|
||
// Find the next object's Sid, and fill in its object information.
|
||
// Note that memory will be allocated via MIDL_user_allocate
|
||
// and must be freed when no longer required.
|
||
//
|
||
|
||
Status = LsapDbFindNextSidWithRight(
|
||
ContainerHandle,
|
||
&EnumerationIndex,
|
||
Privilege,
|
||
SystemAccess,
|
||
(PLSAPR_SID *) &NextElement->Sid
|
||
);
|
||
|
||
//
|
||
// Stop the enumeration if any error or warning occurs. Note
|
||
// that the warning STATUS_NO_MORE_ENTRIES will be returned when
|
||
// we've gone beyond the last index.
|
||
//
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
|
||
//
|
||
// If it failed because it was missing the privilege, continue
|
||
//
|
||
|
||
if (Status == STATUS_NOT_ALL_ASSIGNED) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Since NextElement is not on the list, it will not get
|
||
// freed at the end so we must free it here.
|
||
//
|
||
|
||
MIDL_user_free( NextElement );
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Link the object just found to the front of the enumeration list
|
||
//
|
||
|
||
NextElement->Next = FirstElement;
|
||
FirstElement = NextElement;
|
||
NextElement = NULL;
|
||
EntriesRead++;
|
||
}
|
||
|
||
//
|
||
// If an error other than STATUS_NO_MORE_ENTRIES occurred, return it.
|
||
// If STATUS_NO_MORE_ENTRIES was returned, we have enumerated all of the
|
||
// entries. In this case, return STATUS_SUCCESS if we enumerated at
|
||
// least one entry, otherwise propagate STATUS_NO_MORE_ENTRIES back to
|
||
// the caller.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (Status != STATUS_NO_MORE_ENTRIES) {
|
||
|
||
goto EnumerateSidsError;
|
||
}
|
||
|
||
if (EntriesRead == 0) {
|
||
|
||
goto EnumerateSidsError;
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Some entries were read, allocate an information buffer for returning
|
||
// them.
|
||
//
|
||
|
||
Sids = (PSID *) MIDL_user_allocate( sizeof (PSID) * EntriesRead );
|
||
|
||
if (Sids == NULL) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto EnumerateSidsError;
|
||
}
|
||
|
||
//
|
||
// Memory was successfully allocated for the return buffer.
|
||
// Copy in the enumerated Sids.
|
||
//
|
||
|
||
for (NextElement = FirstElement, Index = 0;
|
||
NextElement != &LastElement;
|
||
NextElement = NextElement->Next, Index++) {
|
||
|
||
ASSERT(Index < EntriesRead);
|
||
|
||
Sids[Index] = NextElement->Sid;
|
||
}
|
||
|
||
EnumerateSidsFinish:
|
||
|
||
//
|
||
// Free the enumeration element structures (if any).
|
||
//
|
||
|
||
for (NextElement = FirstElement; NextElement != &LastElement;) {
|
||
|
||
//
|
||
// If an error has occurred, dispose of memory allocated
|
||
// for any Sids.
|
||
//
|
||
|
||
if (!(NT_SUCCESS(Status) || (Status == STATUS_NO_MORE_ENTRIES))) {
|
||
|
||
if (NextElement->Sid != NULL) {
|
||
|
||
MIDL_user_free(NextElement->Sid);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free the memory allocated for the enumeration element.
|
||
//
|
||
|
||
FreeElement = NextElement;
|
||
NextElement = NextElement->Next;
|
||
|
||
MIDL_user_free(FreeElement);
|
||
}
|
||
|
||
//
|
||
// Fill in return enumeration structure (0 and NULL in error case).
|
||
//
|
||
|
||
DbEnumerationBuffer->EntriesRead = EntriesRead;
|
||
DbEnumerationBuffer->Sids = Sids;
|
||
|
||
return(Status);
|
||
|
||
EnumerateSidsError:
|
||
|
||
//
|
||
// If necessary, free memory allocated for returning the Sids.
|
||
//
|
||
|
||
if (Sids != NULL) {
|
||
|
||
MIDL_user_free( Sids );
|
||
Sids = NULL;
|
||
}
|
||
|
||
goto EnumerateSidsFinish;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
LsarEnumerateAccountsWithUserRight(
|
||
IN LSAPR_HANDLE PolicyHandle,
|
||
IN OPTIONAL PLSAPR_UNICODE_STRING UserRight,
|
||
OUT PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the LSA server RPC worker routine for the
|
||
LsaEnumerateAccountsWithUserRight API.
|
||
|
||
The LsaEnumerateAccounts API returns information about the accounts
|
||
in the target system's Lsa Database. This call requires
|
||
LSA_ENUMERATE_ACCOUNTS access to the Policy object. Since this call
|
||
accesses the privileges of an account, you must have PRIVILEGE_VIEW
|
||
access to the pseudo-privilege object.
|
||
|
||
Arguments:
|
||
|
||
PolicyHandle - Handle from an LsaOpenPolicy call.
|
||
|
||
UserRight - Name of the right that the account must have.
|
||
|
||
EnumerationBuffer - Pointer to an enumeration structure that will receive
|
||
a count of the accounts enumerated on this call and a pointer to
|
||
an array of entries containing information for each enumerated
|
||
account.
|
||
|
||
|
||
Return Values:
|
||
|
||
NTSTATUS - Standard Nt Result Code
|
||
|
||
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
||
to complete the operation.
|
||
|
||
STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
|
||
is returned if no objects are enumerated because the
|
||
EnumerationContext value passed in is too high.
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
LSAP_DB_SID_ENUMERATION_BUFFER DbEnumerationBuffer;
|
||
ULONG MaxLength;
|
||
ULONG Index;
|
||
ULONG SystemAccess = 0;
|
||
LUID PrivilegeValue;
|
||
PLUID Privilege = NULL;
|
||
PULONG Access = NULL;
|
||
ACCESS_MASK GrantedAccess;
|
||
NTSTATUS AccessStatus;
|
||
BOOLEAN GenerateOnClose;
|
||
|
||
|
||
//
|
||
// If no Enumeration Structure or index is provided, return an error.
|
||
//
|
||
|
||
if ( !ARGUMENT_PRESENT(EnumerationBuffer) ) {
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
//
|
||
// Initialize the internal Lsa Database Enumeration Buffer, and
|
||
// the provided Enumeration Buffer to NULL.
|
||
//
|
||
|
||
DbEnumerationBuffer.EntriesRead = 0;
|
||
DbEnumerationBuffer.Sids = NULL;
|
||
EnumerationBuffer->EntriesRead = 0;
|
||
EnumerationBuffer->Information = 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,
|
||
0,
|
||
PolicyObject,
|
||
LSAP_DB_ACQUIRE_LOCK
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Impersonate the caller
|
||
//
|
||
|
||
Status = I_RpcMapWin32Status(RpcImpersonateClient(0));
|
||
if (!NT_SUCCESS(Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Do an access check on with the UserRight security descriptor.
|
||
//
|
||
|
||
Status = NtAccessCheckAndAuditAlarm(
|
||
&LsapState.SubsystemName,
|
||
PolicyHandle,
|
||
&UserRightTypeName,
|
||
&UserRightTypeName,
|
||
UserRightSD,
|
||
PRIVILEGE_VIEW,
|
||
&UserRightGenericMapping,
|
||
FALSE,
|
||
&GrantedAccess,
|
||
&AccessStatus,
|
||
&GenerateOnClose
|
||
);
|
||
|
||
(VOID) RpcRevertToSelf();
|
||
|
||
//
|
||
// Check both error codes
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = AccessStatus;
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If a right was specified, translate it to a privilege or a
|
||
// system access type.
|
||
//
|
||
|
||
|
||
if (UserRight != NULL) {
|
||
|
||
//
|
||
// Convert the user right string into a privilege or a system
|
||
// access flag.
|
||
//
|
||
|
||
for (Index = 0; Index < LSAP_DB_SYSTEM_ACCESS_TYPES; Index++ ) {
|
||
|
||
if (RtlCompareUnicodeString(
|
||
&LsapDbRightAndAccess[Index].UserRight,
|
||
(PUNICODE_STRING) UserRight,
|
||
TRUE ) == 0) { // case insensitive
|
||
|
||
SystemAccess = LsapDbRightAndAccess[Index].SystemAccess;
|
||
Access = &SystemAccess;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Of system access is zero, try looking up the privilege name.
|
||
//
|
||
|
||
if (Access == NULL) {
|
||
Status = LsarLookupPrivilegeValue(
|
||
PolicyHandle,
|
||
(PLSAPR_UNICODE_STRING) UserRight,
|
||
&PrivilegeValue
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
Privilege = &PrivilegeValue;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
//
|
||
// Call general Sid enumeration routine.
|
||
//
|
||
|
||
Status = LsapDbEnumerateSidsWithRight(
|
||
PolicyHandle,
|
||
Privilege,
|
||
Access,
|
||
&DbEnumerationBuffer
|
||
);
|
||
|
||
//
|
||
// Copy the enumerated information to the output. We can use the
|
||
// information actually returned by LsapDbEnumerateSids because it
|
||
// happens to be in exactly the correct form.
|
||
//
|
||
|
||
EnumerationBuffer->EntriesRead = DbEnumerationBuffer.EntriesRead;
|
||
EnumerationBuffer->Information =
|
||
(PLSAPR_ACCOUNT_INFORMATION) DbEnumerationBuffer.Sids;
|
||
|
||
|
||
Cleanup:
|
||
|
||
Status = LsapDbDereferenceObject(
|
||
&PolicyHandle,
|
||
PolicyObject,
|
||
LSAP_DB_RELEASE_LOCK,
|
||
(SECURITY_DB_DELTA_TYPE) 0,
|
||
Status
|
||
);
|
||
|
||
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
LsarEnumerateAccountRights(
|
||
IN LSAPR_HANDLE PolicyHandle,
|
||
IN PLSAPR_SID AccountSid,
|
||
OUT PLSAPR_USER_RIGHT_SET UserRights
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns all the rights of an account. This is done by gathering the
|
||
privileges and system access of an account and translating that into
|
||
an array of strings.
|
||
|
||
Arguments:
|
||
|
||
PolicyHandle - Handle from an LsaOpenPolicyCall. This API requires
|
||
no special access.
|
||
|
||
AccountSid - Sid of account to open.
|
||
|
||
UserRights - receives an array of user rights for the account
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_ACCESS_DENIED - the caller did not have sufficient access to
|
||
return the privileges or system access of the account.
|
||
|
||
STATUS_OBJECT_NAME_NOT_FOUND - the specified account did not exist.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the
|
||
request.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
LSAPR_HANDLE AccountHandle = NULL;
|
||
PLSAPR_PRIVILEGE_SET PrivilegeSet = NULL;
|
||
|
||
ULONG SystemAccess;
|
||
ULONG UserRightCount;
|
||
ULONG UserRightIndex;
|
||
ULONG PrivilegeIndex;
|
||
PUNICODE_STRING UserRightArray;
|
||
PUNICODE_STRING TempString;
|
||
|
||
//
|
||
// Open the account for ACCOUNT_VIEW access
|
||
//
|
||
|
||
Status = LsarOpenAccount(
|
||
PolicyHandle,
|
||
AccountSid,
|
||
ACCOUNT_VIEW,
|
||
&AccountHandle
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Get the system access flags
|
||
//
|
||
|
||
Status = LsarGetSystemAccessAccount(
|
||
AccountHandle,
|
||
&SystemAccess
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Get the privilege information
|
||
//
|
||
|
||
|
||
Status = LsarEnumeratePrivilegesAccount(
|
||
AccountHandle,
|
||
&PrivilegeSet
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Repackage the privileges and system access as user rights
|
||
//
|
||
|
||
UserRightCount = 0;
|
||
|
||
for (PrivilegeIndex = 0;
|
||
PrivilegeIndex < LSAP_DB_SYSTEM_ACCESS_TYPES;
|
||
PrivilegeIndex++ ) {
|
||
|
||
if ((SystemAccess & LsapDbRightAndAccess[PrivilegeIndex].SystemAccess) != 0 ) {
|
||
UserRightCount++;
|
||
}
|
||
}
|
||
|
||
UserRightCount += PrivilegeSet->PrivilegeCount;
|
||
|
||
//
|
||
// If there were no rights, say that and cleanup.
|
||
//
|
||
|
||
if (UserRightCount == 0) {
|
||
|
||
UserRights->Entries = 0;
|
||
UserRights->UserRights = NULL;
|
||
Status = STATUS_SUCCESS;
|
||
goto Cleanup;
|
||
}
|
||
|
||
UserRightArray = (PUNICODE_STRING)
|
||
MIDL_user_allocate(UserRightCount * sizeof(LSAPR_UNICODE_STRING));
|
||
|
||
if (UserRightArray == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Zero this in case we have to clean it up partially.
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
UserRightArray,
|
||
UserRightCount * sizeof(LSAPR_UNICODE_STRING)
|
||
);
|
||
|
||
UserRightIndex = 0;
|
||
for (PrivilegeIndex = 0;
|
||
PrivilegeIndex < PrivilegeSet->PrivilegeCount ;
|
||
PrivilegeIndex++ ) {
|
||
|
||
TempString = NULL;
|
||
Status = LsarLookupPrivilegeName(
|
||
PolicyHandle,
|
||
(PLUID) &PrivilegeSet->Privilege[PrivilegeIndex].Luid,
|
||
(PLSAPR_UNICODE_STRING *) &TempString
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// The name that came back was allocated in two parts, the buffer
|
||
// and the string. Copy the buffer pointer to the return array
|
||
// and free the string structure.
|
||
//
|
||
|
||
UserRightArray[UserRightIndex++] = * TempString;
|
||
MIDL_user_free(TempString);
|
||
|
||
|
||
}
|
||
|
||
//
|
||
// Copy in the system access rights
|
||
for (PrivilegeIndex = 0;
|
||
PrivilegeIndex < LSAP_DB_SYSTEM_ACCESS_TYPES;
|
||
PrivilegeIndex++ ) {
|
||
|
||
if ((SystemAccess & LsapDbRightAndAccess[PrivilegeIndex].SystemAccess) != 0 ) {
|
||
|
||
//
|
||
// Allocate a new string and copy the access name into it.
|
||
//
|
||
|
||
UserRightArray[UserRightIndex] = LsapDbRightAndAccess[PrivilegeIndex].UserRight;
|
||
UserRightArray[UserRightIndex].Buffer = (LPWSTR)
|
||
MIDL_user_allocate(LsapDbRightAndAccess[PrivilegeIndex].UserRight.MaximumLength);
|
||
|
||
if (UserRightArray[UserRightIndex].Buffer == NULL ) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyUnicodeString(
|
||
(PUNICODE_STRING) &UserRightArray[UserRightIndex],
|
||
&LsapDbRightAndAccess[PrivilegeIndex].UserRight
|
||
);
|
||
|
||
UserRightIndex++;
|
||
}
|
||
}
|
||
ASSERT(UserRightCount == UserRightIndex);
|
||
|
||
UserRights->Entries = UserRightCount;
|
||
UserRights->UserRights = (PLSAPR_UNICODE_STRING) UserRightArray;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
|
||
Cleanup:
|
||
|
||
//
|
||
// Cleanup the system rights if we failed
|
||
//
|
||
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (UserRightArray != NULL) {
|
||
|
||
for (UserRightIndex = 0;
|
||
UserRightIndex < UserRightCount ;
|
||
UserRightIndex++) {
|
||
|
||
if (UserRightArray[UserRightIndex].Buffer != NULL) {
|
||
|
||
MIDL_user_free(UserRightArray[UserRightIndex].Buffer);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (AccountHandle != NULL) {
|
||
LsarClose(&AccountHandle);
|
||
}
|
||
if (PrivilegeSet != NULL) {
|
||
MIDL_user_free(PrivilegeSet);
|
||
}
|
||
|
||
return(Status);
|
||
}
|
||
|
||
NTSTATUS
|
||
LsapDbConvertRightsToPrivileges(
|
||
IN PLSAPR_HANDLE PolicyHandle,
|
||
IN PLSAPR_USER_RIGHT_SET UserRights,
|
||
OUT PLSAPR_PRIVILEGE_SET * PrivilegeSet,
|
||
OUT PULONG SystemAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Converts an array of user right strings into a privilege set and a
|
||
system access flag.
|
||
|
||
Arguments:
|
||
|
||
UserRights - Contains an array of strings and a count of those strings.
|
||
|
||
PrivilegeSet - receives a privilege set of those rights corresponding
|
||
to privilges, allocated with MIDL_user_allocate.
|
||
|
||
SystemAccess - receives the access flags specified by the user rights
|
||
|
||
Return Value:
|
||
|
||
STATUS_NO_SUCH_PRIVILEGE - the user right could not be converted into
|
||
a privilege or an access type.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - there was not enough memory to translate
|
||
the rights to privileges.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG PrivilegeCount;
|
||
PLSAPR_PRIVILEGE_SET Privileges = NULL;
|
||
ULONG Access = 0;
|
||
ULONG PrivilegeSetSize;
|
||
ULONG PrivilegeIndex = 0;
|
||
ULONG RightIndex;
|
||
ULONG AccessIndex;
|
||
NTSTATUS Status;
|
||
|
||
PrivilegeSetSize = sizeof(LSAPR_PRIVILEGE_SET) +
|
||
(UserRights->Entries-1) * sizeof(LUID_AND_ATTRIBUTES);
|
||
|
||
|
||
Privileges = (PLSAPR_PRIVILEGE_SET) MIDL_user_allocate(PrivilegeSetSize);
|
||
if (Privileges == NULL) {
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
for (RightIndex = 0;
|
||
RightIndex < UserRights->Entries;
|
||
RightIndex++ ) {
|
||
|
||
//
|
||
// First try to map the right as a privilege
|
||
//
|
||
|
||
if (NT_SUCCESS(LsarLookupPrivilegeValue(
|
||
PolicyHandle,
|
||
(PLSAPR_UNICODE_STRING) &UserRights->UserRights[RightIndex],
|
||
(PLUID) &Privileges->Privilege[PrivilegeIndex].Luid))) {
|
||
Privileges->Privilege[PrivilegeIndex].Attributes = 0;
|
||
PrivilegeIndex++;
|
||
} else {
|
||
|
||
//
|
||
// Try to map it to a system access type
|
||
//
|
||
|
||
for (AccessIndex = 0;
|
||
AccessIndex < LSAP_DB_SYSTEM_ACCESS_TYPES;
|
||
AccessIndex++ ) {
|
||
|
||
if (RtlCompareUnicodeString(
|
||
&LsapDbRightAndAccess[AccessIndex].UserRight,
|
||
(PUNICODE_STRING) &UserRights->UserRights[RightIndex],
|
||
FALSE) == 0) { // case sensistive
|
||
|
||
Access |= LsapDbRightAndAccess[AccessIndex].SystemAccess;
|
||
break;
|
||
}
|
||
}
|
||
if (AccessIndex == LSAP_DB_SYSTEM_ACCESS_TYPES) {
|
||
Status = STATUS_NO_SUCH_PRIVILEGE;
|
||
goto Cleanup;
|
||
}
|
||
|
||
}
|
||
}
|
||
Privileges->PrivilegeCount = PrivilegeIndex;
|
||
*PrivilegeSet = Privileges;
|
||
*SystemAccess = Access;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
Cleanup:
|
||
if (!NT_SUCCESS(Status)) {
|
||
if (Privileges != NULL) {
|
||
MIDL_user_free(Privileges);
|
||
}
|
||
}
|
||
return(Status);
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
LsarAddAccountRights(
|
||
IN LSAPR_HANDLE PolicyHandle,
|
||
IN PLSAPR_SID AccountSid,
|
||
IN PLSAPR_USER_RIGHT_SET UserRights
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds rights to the account specified by the account sid. If the account
|
||
does not exist, it creates the account.
|
||
|
||
Arguments:
|
||
|
||
PolicyHandle - Handle from an LsaOpenPolicy call. The handle must have
|
||
POLICY_CREATE_ACCOUNT access if this is the first call for this
|
||
AccountSid.
|
||
|
||
AccountSid - Sid of account to add rights to
|
||
|
||
UserRights - Array of unicode strings naming rights to add to the
|
||
account.
|
||
|
||
Return Value:
|
||
STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the request
|
||
|
||
STATUS_INVALID_PARAMTER - one of the parameters was not present
|
||
|
||
STATUS_NO_SUCH_PRIVILEGE - One of the user rights was invalid
|
||
|
||
STATUS_ACCESS_DENIED - the caller does not have sufficient access to the
|
||
account to add privileges.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
LSAPR_HANDLE AccountHandle = NULL;
|
||
PLSAPR_PRIVILEGE_SET PrivilegeSet = NULL;
|
||
ULONG SystemAccess;
|
||
ULONG OldSystemAccess;
|
||
BOOLEAN ChangedAccess = FALSE;
|
||
|
||
//
|
||
// Make sure we have all the arguments we need
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(UserRights)) {
|
||
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
if (!ARGUMENT_PRESENT(AccountSid)) {
|
||
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
//
|
||
// Open the account for ACCOUNT_VIEW access
|
||
//
|
||
|
||
Status = LsarOpenAccount(
|
||
PolicyHandle,
|
||
AccountSid,
|
||
ACCOUNT_ADJUST_PRIVILEGES | ACCOUNT_ADJUST_SYSTEM_ACCESS | ACCOUNT_VIEW,
|
||
&AccountHandle
|
||
);
|
||
//
|
||
// If the account did not exist, try to create it.
|
||
//
|
||
|
||
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
Status = LsarCreateAccount(
|
||
PolicyHandle,
|
||
AccountSid,
|
||
ACCOUNT_ADJUST_PRIVILEGES | ACCOUNT_ADJUST_SYSTEM_ACCESS | ACCOUNT_VIEW,
|
||
&AccountHandle
|
||
);
|
||
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
return(Status);
|
||
}
|
||
|
||
Status = LsapDbConvertRightsToPrivileges(
|
||
PolicyHandle,
|
||
UserRights,
|
||
&PrivilegeSet,
|
||
&SystemAccess
|
||
);
|
||
if (!NT_SUCCESS(Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If system access was changed, add it
|
||
//
|
||
|
||
if (SystemAccess != 0) {
|
||
Status = LsarGetSystemAccessAccount(
|
||
AccountHandle,
|
||
&OldSystemAccess
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
SystemAccess = SystemAccess | OldSystemAccess;
|
||
|
||
Status = LsarSetSystemAccessAccount(
|
||
AccountHandle,
|
||
SystemAccess
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
ChangedAccess = TRUE;
|
||
}
|
||
//
|
||
// If privileges were changed, add them.
|
||
//
|
||
|
||
if (PrivilegeSet->PrivilegeCount != 0) {
|
||
|
||
Status = LsarAddPrivilegesToAccount(
|
||
AccountHandle,
|
||
PrivilegeSet
|
||
);
|
||
|
||
}
|
||
|
||
Cleanup:
|
||
//
|
||
// If we didn't make both changes, unroll the one we did
|
||
//
|
||
|
||
|
||
if (!NT_SUCCESS(Status) && ChangedAccess) {
|
||
|
||
//
|
||
// Ignore the error code since this is a last-ditch effort
|
||
//
|
||
|
||
(void) LsarSetSystemAccessAccount(
|
||
AccountHandle,
|
||
OldSystemAccess
|
||
);
|
||
|
||
}
|
||
|
||
if (PrivilegeSet != NULL) {
|
||
MIDL_user_free(PrivilegeSet);
|
||
}
|
||
|
||
if (AccountHandle != NULL) {
|
||
LsarClose(&AccountHandle);
|
||
}
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
LsarRemoveAccountRights(
|
||
IN LSAPR_HANDLE PolicyHandle,
|
||
IN PLSAPR_SID AccountSid,
|
||
IN BOOLEAN AllRights,
|
||
IN PLSAPR_USER_RIGHT_SET UserRights
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes rights to the account specified by the account sid. If the
|
||
AllRights flag is set or if all the rights are removed, the account
|
||
is deleted.
|
||
|
||
Arguments:
|
||
|
||
PolicyHandle - Handle from an LsaOpenPolicy call
|
||
|
||
AccountSid - Sid of account to remove rights from
|
||
|
||
UserRights - Array of unicode strings naming rights to remove from the
|
||
account.
|
||
|
||
Return Value:
|
||
STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the request
|
||
|
||
STATUS_INVALID_PARAMTER - one of the parameters was not present
|
||
|
||
STATUS_NO_SUCH_PRIVILEGE - One of the user rights was invalid
|
||
|
||
STATUS_ACCESS_DENIED - the caller does not have sufficient access to the
|
||
account to add privileges.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
LSAPR_HANDLE AccountHandle = NULL;
|
||
PLSAPR_PRIVILEGE_SET PrivilegeSet = NULL;
|
||
ULONG SystemAccess;
|
||
ULONG OldSystemAccess;
|
||
BOOLEAN ChangedAccess = FALSE;
|
||
PLSAPR_PRIVILEGE_SET FinalPrivilegeSet = NULL;
|
||
|
||
|
||
//
|
||
// Open the account for ACCOUNT_VIEW access
|
||
//
|
||
|
||
Status = LsarOpenAccount(
|
||
PolicyHandle,
|
||
AccountSid,
|
||
ACCOUNT_ADJUST_PRIVILEGES |
|
||
ACCOUNT_ADJUST_SYSTEM_ACCESS |
|
||
ACCOUNT_VIEW |
|
||
DELETE,
|
||
&AccountHandle
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
return(Status);
|
||
}
|
||
|
||
|
||
//
|
||
// Convert the rights to privileges only if they don't want all
|
||
// rights removed. In that case, we don't care.
|
||
//
|
||
|
||
if (AllRights == FALSE) {
|
||
Status = LsapDbConvertRightsToPrivileges(
|
||
PolicyHandle,
|
||
UserRights,
|
||
&PrivilegeSet,
|
||
&SystemAccess
|
||
);
|
||
if (!NT_SUCCESS(Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
} else {
|
||
Status = LsarDeleteObject(
|
||
&AccountHandle
|
||
);
|
||
//
|
||
// If the handle was deleted, zero it so we don't try to
|
||
// close it later.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
AccountHandle = NULL;
|
||
}
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If system access was changed, add it
|
||
//
|
||
|
||
Status = LsarGetSystemAccessAccount(
|
||
AccountHandle,
|
||
&OldSystemAccess
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// After this block of code, SystemAccess should contain the final
|
||
// access for the account.
|
||
//
|
||
|
||
if (SystemAccess != 0) {
|
||
|
||
SystemAccess = OldSystemAccess & ~SystemAccess;
|
||
|
||
Status = LsarSetSystemAccessAccount(
|
||
AccountHandle,
|
||
SystemAccess
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
ChangedAccess = TRUE;
|
||
} else {
|
||
|
||
SystemAccess = OldSystemAccess;
|
||
}
|
||
|
||
//
|
||
// If privileges were changed, add them.
|
||
//
|
||
|
||
if (AllRights || PrivilegeSet->PrivilegeCount != 0) {
|
||
|
||
Status = LsarRemovePrivilegesFromAccount(
|
||
AccountHandle,
|
||
FALSE, // don't remove all rights
|
||
PrivilegeSet
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Check to see if all the privileges have been removed - if so,
|
||
// and system access is 0, delete the account.
|
||
//
|
||
|
||
Status = LsarEnumeratePrivilegesAccount(
|
||
AccountHandle,
|
||
&FinalPrivilegeSet
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
if ((FinalPrivilegeSet->PrivilegeCount == 0) &&
|
||
(SystemAccess == 0)) {
|
||
|
||
//
|
||
// The account has no privileges or system access - delete it.
|
||
//
|
||
|
||
Status = LsarDeleteObject(
|
||
&AccountHandle
|
||
);
|
||
//
|
||
// If the handle was deleted, zero it so we don't try to
|
||
// close it later.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
AccountHandle = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
Cleanup:
|
||
//
|
||
// If we didn't make both changes, unroll the one we did
|
||
//
|
||
|
||
|
||
if (!NT_SUCCESS(Status) && ChangedAccess) {
|
||
|
||
//
|
||
// Ignore the error code since this is a last-ditch effort
|
||
//
|
||
|
||
(void) LsarSetSystemAccessAccount(
|
||
AccountHandle,
|
||
OldSystemAccess
|
||
);
|
||
|
||
}
|
||
|
||
if (PrivilegeSet != NULL) {
|
||
MIDL_user_free(PrivilegeSet);
|
||
}
|
||
|
||
if (AccountHandle != NULL) {
|
||
LsarClose(&AccountHandle);
|
||
}
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
LsarQueryTrustedDomainInfo(
|
||
IN LSAPR_HANDLE PolicyHandle,
|
||
IN PLSAPR_SID TrustedDomainSid,
|
||
IN TRUSTED_INFORMATION_CLASS InformationClass,
|
||
IN PLSAPR_TRUSTED_DOMAIN_INFO * TrustedDomainInformation
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the LSA server RPC worker routine for the
|
||
LsaQueryInfoTrustedDomain API.
|
||
|
||
The LsaQueryInfoTrustedDomain API obtains information from a
|
||
TrustedDomain object. The caller must have access appropriate to the
|
||
information being requested (see InformationClass parameter). It also
|
||
may query the secret object (for the TrustedDomainPasswordInformation
|
||
class).
|
||
|
||
Arguments:
|
||
|
||
PolicyHandle - Handle from an LsaOpenPolicy call.
|
||
|
||
TrustedDomainSid - Sid of domain to query.
|
||
|
||
InformationClass - Specifies the information to be returned.
|
||
|
||
Buffer - Receives a pointer to the buffer returned comtaining the
|
||
requested information. This buffer is allocated by this service
|
||
and must be freed when no longer needed by passing the returned
|
||
value to LsaFreeMemory().
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Standard Nt Result Code
|
||
|
||
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
||
access to complete the operation.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
||
such as memory, to complete the call.
|
||
--*/
|
||
{
|
||
|
||
LSAPR_HANDLE DomainHandle = NULL;
|
||
LSAPR_HANDLE SecretHandle = NULL;
|
||
NTSTATUS Status;
|
||
PLSAPR_TRUSTED_DOMAIN_INFO DomainInfo = NULL;
|
||
PLSAPR_TRUSTED_PASSWORD_INFO PasswordInfo = NULL;
|
||
TRUSTED_INFORMATION_CLASS ClassToUse;
|
||
ULONG DesiredAccess;
|
||
BOOLEAN QueryPassword = FALSE;
|
||
UNICODE_STRING SecretName;
|
||
PLSAPR_CR_CIPHER_VALUE Password = NULL;
|
||
PLSAPR_CR_CIPHER_VALUE OldPassword = NULL;
|
||
|
||
SecretName.Buffer = NULL;
|
||
|
||
ClassToUse = InformationClass;
|
||
switch(InformationClass) {
|
||
|
||
case TrustedPasswordInformation:
|
||
QueryPassword = TRUE;
|
||
DesiredAccess = TRUSTED_QUERY_DOMAIN_NAME;
|
||
ClassToUse = TrustedDomainNameInformation;
|
||
break;
|
||
|
||
case TrustedDomainNameInformation:
|
||
DesiredAccess = TRUSTED_QUERY_DOMAIN_NAME;
|
||
break;
|
||
|
||
case TrustedControllersInformation:
|
||
//
|
||
// This info class is obsolete
|
||
//
|
||
return(STATUS_NOT_IMPLEMENTED);
|
||
break;
|
||
case TrustedPosixOffsetInformation:
|
||
DesiredAccess = TRUSTED_QUERY_POSIX;
|
||
break;
|
||
|
||
}
|
||
|
||
Status = LsarOpenTrustedDomain(
|
||
PolicyHandle,
|
||
TrustedDomainSid,
|
||
DesiredAccess,
|
||
&DomainHandle
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
Status = LsarQueryInfoTrustedDomain(
|
||
DomainHandle,
|
||
ClassToUse,
|
||
&DomainInfo
|
||
);
|
||
|
||
//
|
||
// If the info we wanted was what we queried, cleanup now.
|
||
//
|
||
|
||
if (!QueryPassword) {
|
||
if (NT_SUCCESS(Status)) {
|
||
*TrustedDomainInformation = DomainInfo;
|
||
DomainInfo = NULL;
|
||
}
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Build the secret name for the domain.
|
||
//
|
||
|
||
|
||
//
|
||
// Build the secret name
|
||
//
|
||
|
||
SecretName.Length = DomainInfo->TrustedDomainNameInfo.Name.Length;
|
||
SecretName.Length += (LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH) * sizeof(WCHAR);
|
||
SecretName.MaximumLength = SecretName.Length + sizeof(WCHAR);
|
||
|
||
SecretName.Buffer = (LPWSTR) MIDL_user_allocate(SecretName.MaximumLength);
|
||
if (SecretName.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
SecretName.Buffer,
|
||
LSA_GLOBAL_SECRET_PREFIX,
|
||
LSA_GLOBAL_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
|
||
);
|
||
RtlCopyMemory(
|
||
SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH,
|
||
SSI_SECRET_PREFIX,
|
||
SSI_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
|
||
);
|
||
RtlCopyMemory(
|
||
SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH,
|
||
DomainInfo->TrustedDomainNameInfo.Name.Buffer,
|
||
DomainInfo->TrustedDomainNameInfo.Name.MaximumLength
|
||
);
|
||
|
||
//
|
||
// Free the domain info so we can re-use it lower down
|
||
//
|
||
|
||
LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
|
||
TrustedDomainNameInformation,
|
||
DomainInfo
|
||
);
|
||
DomainInfo = NULL;
|
||
|
||
//
|
||
// Now try to open the secret
|
||
//
|
||
|
||
Status = LsarOpenSecret(
|
||
PolicyHandle,
|
||
(PLSAPR_UNICODE_STRING) &SecretName,
|
||
SECRET_QUERY_VALUE,
|
||
&SecretHandle
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = LsarQuerySecret(
|
||
SecretHandle,
|
||
&Password,
|
||
NULL,
|
||
&OldPassword,
|
||
NULL
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Build a new domain info with the secret information
|
||
//
|
||
|
||
DomainInfo = (PLSAPR_TRUSTED_DOMAIN_INFO)
|
||
MIDL_user_allocate(sizeof(LSAPR_TRUSTED_DOMAIN_INFO));
|
||
|
||
if (DomainInfo == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
DomainInfo->TrustedPasswordInfo.Password = Password;
|
||
DomainInfo->TrustedPasswordInfo.OldPassword = OldPassword;
|
||
|
||
Password = NULL;
|
||
|
||
OldPassword = NULL;
|
||
|
||
*TrustedDomainInformation = DomainInfo;
|
||
DomainInfo = NULL;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
|
||
|
||
Cleanup:
|
||
if (SecretName.Buffer != NULL) {
|
||
MIDL_user_free(SecretName.Buffer);
|
||
}
|
||
|
||
if (DomainHandle != NULL) {
|
||
LsarClose(&DomainHandle);
|
||
}
|
||
|
||
if (SecretHandle != NULL) {
|
||
LsarClose(&SecretHandle);
|
||
}
|
||
|
||
if (DomainInfo != NULL) {
|
||
LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
|
||
ClassToUse,
|
||
DomainInfo
|
||
);
|
||
|
||
}
|
||
if (Password != NULL) {
|
||
LsaIFree_LSAPR_CR_CIPHER_VALUE(Password);
|
||
}
|
||
if (OldPassword != NULL) {
|
||
LsaIFree_LSAPR_CR_CIPHER_VALUE(OldPassword);
|
||
}
|
||
return(Status);
|
||
}
|
||
|
||
NTSTATUS
|
||
LsarSetTrustedDomainInfo(
|
||
IN LSAPR_HANDLE PolicyHandle,
|
||
IN PLSAPR_SID TrustedDomainSid,
|
||
IN TRUSTED_INFORMATION_CLASS InformationClass,
|
||
IN PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the LSA server RPC worker routine for the
|
||
LsaSetInfoTrustedDomain API.
|
||
|
||
The LsaSetInformationTrustedDomain API modifies information in the Trusted
|
||
Domain Object and in the Secret Object. The caller must have access
|
||
appropriate to the information to be changed in the Policy Object, see
|
||
the InformationClass parameter.
|
||
|
||
If the domain does not yet exist and the information class is
|
||
TrustedDomainNameInformation, then the domain is created. If the
|
||
domain exists and the class is TrustedDomainNameInformation, an
|
||
error is returned.
|
||
|
||
Arguments:
|
||
|
||
PolicyHandle - Handle from an LsaOpenPolicy call.
|
||
|
||
TrustedDomainSid - Sid of domain to modify.
|
||
|
||
InformationClass - Specifies the type of information being changed.
|
||
The information types and accesses required to change them are as
|
||
follows:
|
||
|
||
TrustedDomainNameInformation POLICY_TRUST_ADMIN
|
||
TrustedPosixOffsetInformation none
|
||
TrustedPasswordInformation POLICY_CREATE_SECRET
|
||
|
||
Buffer - Points to a structure containing the information appropriate
|
||
to the InformationClass parameter.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Standard Nt Result Code
|
||
|
||
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
||
to complete the operation.
|
||
|
||
Others TBS
|
||
--*/
|
||
{
|
||
LSAPR_HANDLE DomainHandle = NULL;
|
||
LSAPR_HANDLE SecretHandle = NULL;
|
||
TRUSTED_INFORMATION_CLASS ClassToUse;
|
||
ULONG DesiredAccess = 0;
|
||
BOOLEAN SetPassword = FALSE;
|
||
LSAPR_TRUST_INFORMATION TrustInformation;
|
||
PLSAPR_TRUSTED_DOMAIN_INFO DomainInfo = NULL;
|
||
UNICODE_STRING SecretName;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
SecretName.Buffer = NULL;
|
||
|
||
|
||
//
|
||
// If the class is domain name, try to create the domain since you
|
||
// can't change the name of an existing domain.
|
||
//
|
||
|
||
if (InformationClass == TrustedDomainNameInformation) {
|
||
|
||
//
|
||
// Try to create the domain if we have the name information
|
||
//
|
||
|
||
TrustInformation.Name = TrustedDomainInformation->TrustedDomainNameInfo.Name;
|
||
TrustInformation.Sid = TrustedDomainSid;
|
||
|
||
Status = LsarCreateTrustedDomain(
|
||
PolicyHandle,
|
||
&TrustInformation,
|
||
0, // desired access
|
||
&DomainHandle
|
||
);
|
||
|
||
goto Cleanup;
|
||
}
|
||
|
||
if (InformationClass == TrustedPosixOffsetInformation) {
|
||
|
||
//
|
||
// For posix information, we just do the normal set information
|
||
//
|
||
|
||
Status = LsarOpenTrustedDomain(
|
||
PolicyHandle,
|
||
TrustedDomainSid,
|
||
TRUSTED_SET_POSIX,
|
||
&DomainHandle
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
Status = LsarSetInformationTrustedDomain(
|
||
DomainHandle,
|
||
InformationClass,
|
||
TrustedDomainInformation
|
||
);
|
||
goto Cleanup;
|
||
|
||
|
||
}
|
||
if (InformationClass != TrustedPasswordInformation) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// For posix information, we just do the normal set information
|
||
//
|
||
|
||
Status = LsarOpenTrustedDomain(
|
||
PolicyHandle,
|
||
TrustedDomainSid,
|
||
TRUSTED_QUERY_DOMAIN_NAME,
|
||
&DomainHandle
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Query the name of the domain so we know what secret to set
|
||
//
|
||
|
||
Status = LsarQueryInfoTrustedDomain(
|
||
DomainHandle,
|
||
TrustedDomainNameInformation,
|
||
&DomainInfo
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
//
|
||
// Build the secret name
|
||
//
|
||
|
||
SecretName.Length = DomainInfo->TrustedDomainNameInfo.Name.Length;
|
||
SecretName.Length += (LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH) * sizeof(WCHAR);
|
||
SecretName.MaximumLength = SecretName.Length + sizeof(WCHAR);
|
||
|
||
SecretName.Buffer = (LPWSTR) MIDL_user_allocate(SecretName.MaximumLength);
|
||
if (SecretName.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
SecretName.Buffer,
|
||
LSA_GLOBAL_SECRET_PREFIX,
|
||
LSA_GLOBAL_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
|
||
);
|
||
RtlCopyMemory(
|
||
SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH,
|
||
SSI_SECRET_PREFIX,
|
||
SSI_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
|
||
);
|
||
RtlCopyMemory(
|
||
SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH,
|
||
DomainInfo->TrustedDomainNameInfo.Name.Buffer,
|
||
DomainInfo->TrustedDomainNameInfo.Name.MaximumLength
|
||
);
|
||
|
||
//
|
||
// Open the secret. If that fails, try to create it.
|
||
//
|
||
|
||
Status = LsarOpenSecret(
|
||
PolicyHandle,
|
||
(PLSAPR_UNICODE_STRING) &SecretName,
|
||
SECRET_SET_VALUE,
|
||
&SecretHandle
|
||
);
|
||
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
Status = LsarCreateSecret(
|
||
PolicyHandle,
|
||
(PLSAPR_UNICODE_STRING) &SecretName,
|
||
SECRET_SET_VALUE,
|
||
&SecretHandle
|
||
);
|
||
}
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
Status = LsarSetSecret(
|
||
SecretHandle,
|
||
TrustedDomainInformation->TrustedPasswordInfo.Password,
|
||
TrustedDomainInformation->TrustedPasswordInfo.OldPassword
|
||
);
|
||
|
||
|
||
|
||
|
||
Cleanup:
|
||
if (SecretName.Buffer != NULL) {
|
||
MIDL_user_free(SecretName.Buffer);
|
||
}
|
||
|
||
if (DomainInfo != NULL) {
|
||
LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
|
||
TrustedDomainNameInformation,
|
||
DomainInfo
|
||
);
|
||
}
|
||
if (DomainHandle != NULL) {
|
||
LsarClose(&DomainHandle);
|
||
}
|
||
if (SecretHandle != NULL) {
|
||
LsarClose(&SecretHandle);
|
||
}
|
||
|
||
return(Status);
|
||
}
|
||
|
||
NTSTATUS
|
||
LsarDeleteTrustedDomain(
|
||
IN LSAPR_HANDLE PolicyHandle,
|
||
IN PLSAPR_SID TrustedDomainSid
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine deletes a trusted domain and the associated secret.
|
||
|
||
Arguments:
|
||
|
||
PolicyHandle - Handle from an LsaOpenPolicy call.
|
||
|
||
TrustedDomainSid - Sid of domain to delete
|
||
|
||
Return Value:
|
||
|
||
STATUS_ACCESS_DENIED - caller has insufficient access to delete
|
||
the requested domain.
|
||
|
||
STATUS_OBJECT_NAME_NOT_FOUND - The requested domain does not exist.
|
||
|
||
--*/
|
||
|
||
{
|
||
LSAPR_HANDLE DomainHandle = NULL;
|
||
LSAPR_HANDLE SecretHandle = NULL;
|
||
UNICODE_STRING SecretName;
|
||
PLSAPR_TRUSTED_DOMAIN_INFO DomainInfo = NULL;
|
||
NTSTATUS Status;
|
||
|
||
|
||
SecretName.Buffer = NULL;
|
||
|
||
//
|
||
// Open the domain so we can find its name (to delete the secret)
|
||
// and then delete it.
|
||
//
|
||
|
||
|
||
Status = LsarOpenTrustedDomain(
|
||
PolicyHandle,
|
||
TrustedDomainSid,
|
||
TRUSTED_QUERY_DOMAIN_NAME | DELETE,
|
||
&DomainHandle
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Query the domain name so we can delete the secret
|
||
//
|
||
|
||
Status = LsarQueryInfoTrustedDomain(
|
||
DomainHandle,
|
||
TrustedDomainNameInformation,
|
||
&DomainInfo
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Build the secret name
|
||
//
|
||
|
||
SecretName.Length = DomainInfo->TrustedDomainNameInfo.Name.Length;
|
||
SecretName.Length += (LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH) * sizeof(WCHAR);
|
||
SecretName.MaximumLength = SecretName.Length + sizeof(WCHAR);
|
||
|
||
SecretName.Buffer = (LPWSTR) MIDL_user_allocate(SecretName.MaximumLength);
|
||
if (SecretName.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
SecretName.Buffer,
|
||
LSA_GLOBAL_SECRET_PREFIX,
|
||
LSA_GLOBAL_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
|
||
);
|
||
RtlCopyMemory(
|
||
SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH,
|
||
SSI_SECRET_PREFIX,
|
||
SSI_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
|
||
);
|
||
RtlCopyMemory(
|
||
SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH,
|
||
DomainInfo->TrustedDomainNameInfo.Name.Buffer,
|
||
DomainInfo->TrustedDomainNameInfo.Name.MaximumLength
|
||
);
|
||
|
||
//
|
||
// Delete the domain
|
||
//
|
||
|
||
Status = LsarDeleteObject(&DomainHandle);
|
||
if (Status != STATUS_SUCCESS) {
|
||
goto Cleanup;
|
||
}
|
||
//
|
||
// Since we successfully deleted the secret, set it to zero so we don't
|
||
// try to close it later.
|
||
//
|
||
|
||
DomainHandle = NULL;
|
||
|
||
//
|
||
// Now try to open the secret and delete it.
|
||
//
|
||
|
||
Status = LsarOpenSecret(
|
||
PolicyHandle,
|
||
(PLSAPR_UNICODE_STRING) &SecretName,
|
||
DELETE,
|
||
&SecretHandle
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// If the secret does not exist, that just means that the password
|
||
// was never set.
|
||
//
|
||
|
||
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = LsarDeleteObject(&SecretHandle);
|
||
|
||
//
|
||
// If we successfully deleted the secret, set it to zero so we don't
|
||
// try to close it later.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
SecretHandle = NULL;
|
||
}
|
||
|
||
Cleanup:
|
||
if (SecretHandle != NULL) {
|
||
LsarClose(&SecretHandle);
|
||
}
|
||
|
||
if (DomainHandle != NULL) {
|
||
LsarClose(&DomainHandle);
|
||
}
|
||
|
||
if (DomainInfo != NULL) {
|
||
LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
|
||
TrustedDomainNameInformation,
|
||
DomainInfo
|
||
);
|
||
}
|
||
if (SecretName.Buffer != NULL) {
|
||
MIDL_user_free(SecretName.Buffer);
|
||
}
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
LsarStorePrivateData(
|
||
IN LSAPR_HANDLE PolicyHandle,
|
||
IN PLSAPR_UNICODE_STRING KeyName,
|
||
IN OPTIONAL PLSAPR_CR_CIPHER_VALUE EncryptedData
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine stores a secret under the name KeyName. If the password
|
||
is not present, the secret is deleted
|
||
|
||
Arguments:
|
||
|
||
PolicyHandle - Handle from an LsaOpenPolicyCall. If this is the
|
||
first call, it requres POLICY_CREATE_SECRET access.
|
||
|
||
KeyName - Name to store the secret under.
|
||
|
||
EncryptedData - private data encrypted with session key.
|
||
|
||
Return Value:
|
||
|
||
STATUS_ACCESS_DENIED - caller has insufficient privilege to set
|
||
the workstation password.
|
||
|
||
--*/
|
||
|
||
{
|
||
LSAPR_HANDLE SecretHandle = NULL;
|
||
NTSTATUS Status;
|
||
ULONG DesiredAccess;
|
||
BOOLEAN DeletePassword = FALSE;
|
||
|
||
if (ARGUMENT_PRESENT(EncryptedData)) {
|
||
DesiredAccess = SECRET_SET_VALUE;
|
||
} else {
|
||
DesiredAccess = DELETE;
|
||
DeletePassword = TRUE;
|
||
}
|
||
|
||
|
||
Status = LsarOpenSecret(
|
||
PolicyHandle,
|
||
KeyName,
|
||
DesiredAccess,
|
||
&SecretHandle
|
||
);
|
||
|
||
//
|
||
// If the secret didn't exist, and we aren't trying to delete it, create it.
|
||
//
|
||
|
||
if ((Status == STATUS_OBJECT_NAME_NOT_FOUND) &&
|
||
(!DeletePassword)) {
|
||
Status = LsarCreateSecret(
|
||
PolicyHandle,
|
||
KeyName,
|
||
SECRET_SET_VALUE,
|
||
&SecretHandle
|
||
);
|
||
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
if (DeletePassword) {
|
||
Status = LsarDeleteObject(&SecretHandle);
|
||
|
||
//
|
||
// If we succeeded, zero the handle so we don't try to close
|
||
// it later.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
SecretHandle = NULL;
|
||
}
|
||
|
||
} else {
|
||
|
||
Status = LsarSetSecret(
|
||
SecretHandle,
|
||
EncryptedData,
|
||
EncryptedData
|
||
);
|
||
|
||
}
|
||
|
||
Cleanup:
|
||
if (SecretHandle != NULL ) {
|
||
LsarClose(&SecretHandle);
|
||
}
|
||
return(Status);
|
||
}
|
||
|
||
NTSTATUS
|
||
LsarRetrievePrivateData(
|
||
IN LSAPR_HANDLE PolicyHandle,
|
||
IN PLSAPR_UNICODE_STRING KeyName,
|
||
IN OUT PLSAPR_CR_CIPHER_VALUE *EncryptedData
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the workstation password stored in the
|
||
KeyName secret.
|
||
|
||
Arguments:
|
||
|
||
PolicyHandle - Handle from an LsaOpenPolicyCall
|
||
|
||
KeyName - Name of secret to retrieve
|
||
|
||
EncryptedData - Receives data encrypted with session key
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_ACCESS_DENIED - caller has insufficient access to get the
|
||
workstation password.
|
||
|
||
STATUS_OBJECT_NAME_NOT_FOUND - there is no workstation password.
|
||
|
||
--*/
|
||
|
||
{
|
||
LSAPR_HANDLE SecretHandle = NULL;
|
||
NTSTATUS Status;
|
||
|
||
|
||
Status = LsarOpenSecret(
|
||
PolicyHandle,
|
||
KeyName,
|
||
SECRET_QUERY_VALUE,
|
||
&SecretHandle
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = LsarQuerySecret(
|
||
SecretHandle,
|
||
EncryptedData,
|
||
NULL,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
Cleanup:
|
||
if (SecretHandle != NULL ) {
|
||
LsarClose(&SecretHandle);
|
||
}
|
||
return(Status);
|
||
}
|