/*++ 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 // required by logonmsv.h #include // 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 // // "\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 // // "\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); }