mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
13138 lines
366 KiB
13138 lines
366 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dblookup.c
|
|
|
|
Abstract:
|
|
|
|
LSA Database - Lookup Sid and Name routines
|
|
|
|
NOTE: This module should remain as portable code that is independent
|
|
of the implementation of the LSA Database. As such, it is
|
|
permitted to use only the exported LSA Database interfaces
|
|
contained in db.h and NOT the private implementation
|
|
dependent functions in dbp.h.
|
|
|
|
Author:
|
|
|
|
Scott Birrell (ScottBi) November 27, 1992
|
|
|
|
Environment:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "lsasrvp.h"
|
|
#include "ausrvp.h"
|
|
#include "dbp.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Lsa Lookup Sid and Name Private Global State Variables // //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
LARGE_INTEGER LsapDbLookupTimeout;
|
|
HANDLE LsapDbLookupCompleteEvent = NULL;
|
|
HANDLE LsapDbLookupStartedEvent = NULL;
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Lsa Lookup Sid and Name Routines //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
NTSTATUS
|
|
LsarLookupSids(
|
|
IN LSAPR_HANDLE PolicyHandle,
|
|
IN PLSAPR_SID_ENUM_BUFFER SidEnumBuffer,
|
|
OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
|
|
OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the LSA Server worker routine for the LsaLookupSids
|
|
API.
|
|
|
|
The LsaLookupSids API attempts to find names corresponding to Sids.
|
|
If a name can not be mapped to a Sid, the Sid is converted to character
|
|
form. The caller must have POLICY_LOOKUP_NAMES access to the Policy
|
|
object.
|
|
|
|
WARNING: This routine allocates memory for its output. The caller is
|
|
responsible for freeing this memory after use. See description of the
|
|
Names parameter.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
SidEnumBuffer - Pointer to an enumeration buffer containing a count
|
|
and a pointer to an array of Count pointers to Sids to be mapped
|
|
to names. The Sids may be well_known SIDs, SIDs of User accounts
|
|
Group Accounts, Alias accounts, or Domains.
|
|
|
|
ReferencedDomains - Receives a pointer to a structure describing the
|
|
domains used for the translation. The entries in this structure
|
|
are referenced by the structure returned via the Names parameter.
|
|
Unlike the Names parameter, which contains an array entry
|
|
for (each translated name, this strutcure will only contain
|
|
component for each domain utilized in the translation.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
TranslatedNames - Pointer to a structure which will reference an array
|
|
records describing each translated name. The nth entry in this array
|
|
provides a translation for the nth entry in the Sids parameter.
|
|
|
|
All of the returned names will be isolated names or NULL strings
|
|
(domain names are returned as NULL strings). If the caller needs
|
|
composite names, they can be generated by prepending the
|
|
isolated name with the domain name and a backslash. For example,
|
|
if (the name Sally is returned, and it is from the domain Manufact,
|
|
then the composite name would be "Manufact" + "\" + "Sally" or
|
|
"Manufact\Sally".
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
If a Sid is not translatable, then the following will occur:
|
|
|
|
1) If the SID's domain is known, then a reference domain record
|
|
will be generated with the domain's name. In this case, the
|
|
name returned via the Names parameter is a Unicode representation
|
|
of the relative ID of the account, such as "(3cmd14)" or the null
|
|
string, if the Sid is that of a domain. So, you might end up
|
|
with a resultant name of "Manufact\(314) for the example with
|
|
Sally above, if Sally's relative id is 314.
|
|
|
|
2) If not even the SID's domain could be located, then a full
|
|
Unicode representation of the SID is generated and no domain
|
|
record is referenced. In this case, the returned string might
|
|
be something like: "(S-1-672194-21-314)".
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on this
|
|
machine. Values of this field are are follows:
|
|
|
|
LsapLookupWksta - First Level Lookup performed on a workstation
|
|
normally configured for Windows-Nt. The lookup searches the
|
|
Well-Known Sids/Names, and the Built-in Domain and Account Domain
|
|
in the local SAM Database. If not all Sids or Names are
|
|
identified, performs a "handoff" of a Second level Lookup to the
|
|
LSA running on a Controller for the workstation's Primary Domain
|
|
(if any).
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Sids or Names are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
MappedCount - Pointer to location that contains a count of the Sids
|
|
mapped so far. On exit, this will be updated.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully and all Sids have
|
|
been translated to names.
|
|
|
|
STATUS_SOME_NOT_MAPPED - At least one of the Sids provided was
|
|
translated to a Name, but not all Sids could be translated. This
|
|
is a success status.
|
|
|
|
STATUS_NONE_MAPPED - None of the Sids provided could be translated
|
|
to names. This is an error status, but output is returned. Such
|
|
output includes partial translations of Sids whose domain could
|
|
be identified, together with their Relative Id in Unicode format,
|
|
and character representations of Sids whose domain could not
|
|
be identified.
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_DOMAIN_CTRLR_CONFIG_ERROR - Target machine not configured
|
|
as a DC when expected.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
such as memory to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, SecondaryStatus;
|
|
PLSAPR_SID *Sids = (PLSAPR_SID *) SidEnumBuffer->SidInfo;
|
|
ULONG Count = SidEnumBuffer->Entries;
|
|
BOOLEAN PolicyHandleReferencedHere = FALSE;
|
|
PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
|
|
PPOLICY_LSA_SERVER_ROLE PolicyLsaServerRoleInfo = NULL;
|
|
PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
|
|
LSA_HANDLE ControllerPolicyHandle = NULL;
|
|
ULONG DomainIndex;
|
|
ULONG SidIndex;
|
|
LSAPR_TRUST_INFORMATION TrustInformation;
|
|
PLSAPR_TRANSLATED_NAME OutputNames = NULL;
|
|
ULONG OutputNamesLength;
|
|
PLSAPR_TRUST_INFORMATION Domains = NULL;
|
|
ULONG CompletelyUnmappedCount = Count;
|
|
ULONG LocalDomainsToSearch = 0;
|
|
BOOLEAN AlreadyTranslated = FALSE;
|
|
LUID LogonId;
|
|
|
|
|
|
|
|
SecondaryStatus = STATUS_SUCCESS;
|
|
|
|
|
|
//
|
|
// Acquire the Lsa Database lock. Verify that the connection handle is
|
|
// valid, is of the expected type and has all of the desired accesses
|
|
// granted. Reference the handle.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
PolicyHandle,
|
|
POLICY_LOOKUP_NAMES,
|
|
PolicyObject,
|
|
LSAP_DB_ACQUIRE_LOCK
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
//
|
|
// We can dereference the original PolicyHandle and release the lock on
|
|
// the LSA Database: if we need to access the database again, we'll
|
|
// use the trusted Lsa handle and the appropriate API will take
|
|
// the lock as required.
|
|
//
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&PolicyHandle,
|
|
PolicyObject,
|
|
LSAP_DB_RELEASE_LOCK,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
|
|
TranslatedNames->Entries = SidEnumBuffer->Entries;
|
|
TranslatedNames->Names = NULL;
|
|
*ReferencedDomains = NULL;
|
|
|
|
|
|
//
|
|
// Verify that all of the Sids passed in are syntactically valid.
|
|
//
|
|
|
|
for (SidIndex = 0; SidIndex < Count; SidIndex++) {
|
|
|
|
if ((Sids[SidIndex] != NULL) && RtlValidSid( (PSID) Sids[SidIndex])) {
|
|
|
|
continue;
|
|
}
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
//
|
|
// Allocate Output Names array buffer. For now don't place its address on
|
|
// the Free List as this buffer contains others that will be placed on
|
|
// that list and the order of freeing is unknown.
|
|
//
|
|
|
|
OutputNamesLength = Count * sizeof(LSA_TRANSLATED_NAME);
|
|
OutputNames = MIDL_user_allocate(OutputNamesLength);
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (OutputNames == NULL) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
TranslatedNames->Entries = SidEnumBuffer->Entries;
|
|
TranslatedNames->Names = OutputNames;
|
|
|
|
|
|
//
|
|
// Initialize Output Names array, marking Sid Use as unknown and
|
|
// specifying negative DomainIndex.
|
|
//
|
|
|
|
RtlZeroMemory( OutputNames, OutputNamesLength);
|
|
|
|
for (SidIndex = 0; SidIndex < Count; SidIndex++) {
|
|
|
|
OutputNames[SidIndex].Use = SidTypeUnknown;
|
|
OutputNames[SidIndex].DomainIndex = LSA_UNKNOWN_INDEX;
|
|
}
|
|
|
|
//
|
|
// Create an empty Referenced Domain List.
|
|
//
|
|
|
|
Status = LsapDbLookupCreateListReferencedDomains( ReferencedDomains, 0 );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
if ( Count == 1 ) {
|
|
|
|
PUNICODE_STRING AccountName;
|
|
PUNICODE_STRING AuthorityName;
|
|
PSID UserSid;
|
|
PSID DomainSid = NULL;
|
|
ULONG Rid;
|
|
PLSAP_LOGON_SESSION LogonSession;
|
|
PTOKEN_USER TokenUserInformation;
|
|
|
|
//
|
|
// Let's see if we're trying to look up the currently logged on
|
|
// user.
|
|
//
|
|
//
|
|
// TokenUserInformation from this call must be freed by calling
|
|
// LsapFreeLsaHeap().
|
|
//
|
|
|
|
Status = LsapQueryClientInfo(
|
|
&TokenUserInformation,
|
|
&LogonId
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
goto NormalLookupPath;
|
|
}
|
|
|
|
if ( RtlEqualSid( TokenUserInformation->User.Sid, Sids[0] )) {
|
|
|
|
LsapFreeLsaHeap( TokenUserInformation );
|
|
|
|
//
|
|
// Got a match. Get the username and domain information
|
|
// from the LogonId
|
|
//
|
|
|
|
LsapAuLock();
|
|
|
|
LogonSession = LsapGetLogonSession ( &LogonId, FALSE );
|
|
|
|
//
|
|
// During setup, we may get NULL returned for the logon session.
|
|
//
|
|
|
|
if (LogonSession == NULL) {
|
|
|
|
LsapAuUnlock();
|
|
goto NormalLookupPath;
|
|
}
|
|
|
|
UserSid = LogonSession->UserSid;
|
|
AccountName = LogonSession->AccountName;
|
|
AuthorityName = LogonSession->AuthorityName;
|
|
|
|
//
|
|
// DomainSid will be allocated for us, free with MIDL_user_free
|
|
//
|
|
|
|
Status = LsapSplitSid( UserSid, &DomainSid, &Rid );
|
|
|
|
if ( !NT_SUCCESS(Status)) {
|
|
LsapAuUnlock();
|
|
goto NormalLookupPath;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
&TrustInformation.Name,
|
|
AuthorityName,
|
|
sizeof( UNICODE_STRING )
|
|
);
|
|
|
|
TrustInformation.Sid = DomainSid;
|
|
|
|
//
|
|
// Fill in the Output Translated Name structure. Note that the
|
|
// buffers for the SID and Unicode Name must be allocated via
|
|
// MIDL_user_allocate() since they will be freed by the calling
|
|
// RPC server stub routine lsarpc_LsarLookupSids() after marshalling.
|
|
//
|
|
|
|
OutputNames[0].Use = SidTypeUser;
|
|
OutputNames[0].DomainIndex = 0;
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
NULL,
|
|
(PUNICODE_STRING) &OutputNames[0].Name,
|
|
AccountName
|
|
);
|
|
|
|
//
|
|
// Username, AccountName, and UserSid have all been copied
|
|
// from the LogonSession structure, so we can release the AuLock now.
|
|
//
|
|
|
|
LsapAuUnlock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
MIDL_user_free(DomainSid);
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
//
|
|
// Make an entry in the list of Referenced Domains.
|
|
//
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
*ReferencedDomains,
|
|
&TrustInformation,
|
|
(PLONG) &OutputNames[0].DomainIndex
|
|
);
|
|
|
|
ASSERT( OutputNames[0].DomainIndex == 0 );
|
|
|
|
//
|
|
// DomainSid has been copied, free it now
|
|
//
|
|
|
|
MIDL_user_free( DomainSid );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto NormalLookupPath;
|
|
}
|
|
|
|
*MappedCount = 1;
|
|
|
|
return( STATUS_SUCCESS );
|
|
|
|
} else {
|
|
|
|
LsapFreeLsaHeap( TokenUserInformation );
|
|
}
|
|
}
|
|
|
|
NormalLookupPath:
|
|
|
|
//
|
|
// The local domains to be searched always include the Accounts
|
|
// domain. For initial lookup targets only, the BUILT_IN domain is
|
|
// also searched.
|
|
//
|
|
|
|
LocalDomainsToSearch = LSAP_DB_SEARCH_ACCOUNT_DOMAIN;
|
|
|
|
if (LookupLevel == LsapLookupWksta) {
|
|
|
|
LocalDomainsToSearch |= LSAP_DB_SEARCH_BUILT_IN_DOMAIN;
|
|
|
|
//
|
|
// This is the lowest Lookup Level, normally targeted at a
|
|
// Workstation but possibly targeted at a DC. Make a first pass of
|
|
// the array of Sids to identify any well-known Isolated Sids. These
|
|
// are Well Known Sids that do not belong to a real domain.
|
|
//
|
|
|
|
Status = LsapDbLookupIsolatedWellKnownSids(
|
|
Count,
|
|
Sids,
|
|
*ReferencedDomains,
|
|
TranslatedNames,
|
|
MappedCount,
|
|
&CompletelyUnmappedCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
//
|
|
// If all Sids are now mapped or partially mapped, finish.
|
|
//
|
|
|
|
if (CompletelyUnmappedCount == (ULONG) 0) {
|
|
goto LookupSidsFinish;
|
|
}
|
|
}
|
|
|
|
//
|
|
// There are some remaining completely unmapped Sids. They may belong to
|
|
// a local SAM Domain. Currently, there are two such domains, the
|
|
// Built-in Domain and the Accounts Domain. For initial Lookup Level
|
|
// we search both of these domains. For higher Lookup Levels we search
|
|
// only the Accounts domain.
|
|
//
|
|
|
|
Status = LsapDbLookupSidsInLocalDomains(
|
|
Count,
|
|
Sids,
|
|
*ReferencedDomains,
|
|
TranslatedNames,
|
|
MappedCount,
|
|
&CompletelyUnmappedCount,
|
|
LocalDomainsToSearch
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
//
|
|
// If all Sids are now mapped or partially mapped, finish.
|
|
//
|
|
|
|
if (CompletelyUnmappedCount == (ULONG) 0) {
|
|
goto LookupSidsFinish;
|
|
}
|
|
|
|
//
|
|
// Not all of the Sids have been identified in the local domains(s).
|
|
// The next step in the search depends on the level of this lookup
|
|
// and how we are configured as follows:
|
|
//
|
|
// Lookup Level Configuration Lookup search next
|
|
//
|
|
// LsapLookupWksta Win Nt Primary Domain
|
|
// LanMan Nt Trusted Domains
|
|
//
|
|
// LsapLookupPDC Win Nt error
|
|
// LanMan Nt Trusted Domains
|
|
//
|
|
// LsaLookupTDL Win Nt error
|
|
// LanMan Nt none
|
|
//
|
|
|
|
if (LookupLevel == LsapLookupWksta) {
|
|
|
|
if (LsapProductType != NtProductLanManNt) {
|
|
|
|
Status = LsarQueryInformationPolicy(
|
|
LsapPolicyHandle,
|
|
PolicyPrimaryDomainInformation,
|
|
(PLSAPR_POLICY_INFORMATION *) &PolicyPrimaryDomainInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
//
|
|
// If there is no Primary Domain as in the case of a WORKGROUP,
|
|
// just finish up. Set a default result code STATUS_SUCCESS. This
|
|
// will be translated to STATUS_SOME_NOT_MAPPED or STATUS_NONE_MAPPED
|
|
// as appropriate.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (PolicyPrimaryDomainInfo->Sid == NULL) {
|
|
goto LookupSidsFinish;
|
|
}
|
|
|
|
//
|
|
// There is a Primary Domain. Search it for Sids. Since a
|
|
// Primary Domain is also a Trusted Domain, we use the
|
|
// Trusted Domain search routine. This routine will "hand off"
|
|
// the search to a Domain Controller's LSA.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
&TrustInformation.Name,
|
|
&PolicyPrimaryDomainInfo->Name,
|
|
sizeof( UNICODE_STRING)
|
|
);
|
|
|
|
TrustInformation.Sid = (PSID) PolicyPrimaryDomainInfo->Sid;
|
|
|
|
Status = LsapDbLookupSidsInPrimaryDomain(
|
|
Count,
|
|
Sids,
|
|
&TrustInformation,
|
|
*ReferencedDomains,
|
|
TranslatedNames,
|
|
LsapLookupPDC,
|
|
MappedCount,
|
|
&CompletelyUnmappedCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
goto LookupSidsFinish;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We reach here in two cases:
|
|
//
|
|
// * Initial Level lookups targeted at DC's
|
|
// * Higher Level Lookups (must be targeted at DC's)
|
|
//
|
|
// For the highest level lookup, that on an individual TDC, there
|
|
// is no more searching to do, since we have already searched the
|
|
// Accounts Domain and we do not follow trust relationships on DC's
|
|
// beyond one level.
|
|
//
|
|
|
|
if (LookupLevel == LsapLookupTDL) {
|
|
|
|
goto LookupSidsFinish;
|
|
}
|
|
|
|
//
|
|
// We are either the initial target of the lookup but not configured
|
|
// as a workstation, or we are the target of a Primary Domain
|
|
// level lookup. In either case, we must be configured as a DC.
|
|
//
|
|
|
|
Status = STATUS_DOMAIN_CTRLR_CONFIG_ERROR;
|
|
|
|
if (LsapProductType != NtProductLanManNt) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
//
|
|
// Obtain the Trusted Domain List and search all Trusted Domains
|
|
// except ourselves.
|
|
//
|
|
|
|
Status = LsapDbLookupSidsInTrustedDomains(
|
|
Count,
|
|
Sids,
|
|
(PLSAPR_REFERENCED_DOMAIN_LIST) *ReferencedDomains,
|
|
TranslatedNames,
|
|
LsapLookupTDL,
|
|
MappedCount,
|
|
&CompletelyUnmappedCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
|
|
LookupSidsFinish:
|
|
|
|
//
|
|
// If there are any unknown Sids (including partially mapped Sids)
|
|
// we need to translate them to character form. We do this translation
|
|
// at the lowest lookup level in all non-error cases and also in the
|
|
// error case where none were mapped.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if ((*MappedCount < Count) &&
|
|
(LookupLevel == LsapLookupWksta) &&
|
|
(AlreadyTranslated == FALSE)) {
|
|
|
|
AlreadyTranslated = TRUE;
|
|
|
|
//
|
|
// The remaining unmapped Sids are unknown. They are either
|
|
// completely unmapped, i.e. their domain is unknown, or
|
|
// partially unmapped, their domain being known but their Rid
|
|
// not being recognized. For completely unmapped Sids, translate
|
|
// the entire Sid to character form. For partially unmapped
|
|
// Sids, translate the Relative Id only to character form.
|
|
//
|
|
|
|
Status = LsapDbLookupTranslateUnknownSids(
|
|
Count,
|
|
Sids,
|
|
*ReferencedDomains,
|
|
TranslatedNames,
|
|
*MappedCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsError;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If some but not all Sids were mapped, return informational status
|
|
// STATUS_SOME_NOT_MAPPED. If no Sids were mapped, return error
|
|
// STATUS_NONE_MAPPED.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (*MappedCount < Count) {
|
|
|
|
Status = STATUS_SOME_NOT_MAPPED;
|
|
|
|
if (*MappedCount == 0) {
|
|
|
|
Status = STATUS_NONE_MAPPED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Policy Primary Domain Info
|
|
//
|
|
|
|
if (PolicyPrimaryDomainInfo != NULL) {
|
|
|
|
LsaIFree_LSAPR_POLICY_INFORMATION(
|
|
PolicyPrimaryDomainInformation,
|
|
(PLSAPR_POLICY_INFORMATION) PolicyPrimaryDomainInfo
|
|
);
|
|
|
|
PolicyPrimaryDomainInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// Temporary debug aid. Trying to find a recurring problem on MIPS
|
|
// free builds on printlab6 running spool stress. This should access
|
|
// violate if the problem has been encountered.
|
|
//
|
|
{ // Temporary debug
|
|
ULONG TmpDiag;
|
|
if (TranslatedNames->Names != NULL) {
|
|
TmpDiag = (TranslatedNames->Names[0].DomainIndex);
|
|
}
|
|
} // end temporary debug
|
|
|
|
return(Status);
|
|
|
|
LookupSidsError:
|
|
|
|
//
|
|
// If the LookupLevel is the lowest (Workstation Level, free up
|
|
// the Names and Referenced Domains arrays.
|
|
//
|
|
|
|
if (LookupLevel == LsapLookupWksta) {
|
|
|
|
//
|
|
// If necessary, free the Names array.
|
|
//
|
|
|
|
if (TranslatedNames->Names != NULL) {
|
|
|
|
OutputNames = TranslatedNames->Names;
|
|
|
|
for (SidIndex = 0; SidIndex < Count; SidIndex++ ) {
|
|
|
|
if (OutputNames[SidIndex].Name.Buffer != NULL) {
|
|
|
|
MIDL_user_free( OutputNames[SidIndex].Name.Buffer );
|
|
OutputNames[SidIndex].Name.Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
MIDL_user_free( TranslatedNames->Names );
|
|
TranslatedNames->Names = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Referenced Domain List.
|
|
//
|
|
|
|
if (*ReferencedDomains != NULL) {
|
|
|
|
Domains = (*ReferencedDomains)->Domains;
|
|
|
|
if (Domains != NULL) {
|
|
|
|
for (DomainIndex = 0;
|
|
DomainIndex < (*ReferencedDomains)->Entries;
|
|
DomainIndex++) {
|
|
|
|
if (Domains[ DomainIndex ].Name.Buffer != NULL) {
|
|
|
|
MIDL_user_free( Domains[ DomainIndex ].Name.Buffer );
|
|
Domains[ DomainIndex ].Name.Buffer == NULL;
|
|
}
|
|
|
|
if (Domains[ DomainIndex ].Sid != NULL) {
|
|
|
|
MIDL_user_free( Domains[ DomainIndex ].Sid );
|
|
Domains[ DomainIndex ].Sid == NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
MIDL_user_free( *ReferencedDomains );
|
|
*ReferencedDomains = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the primary status was a success code, but the secondary
|
|
// status was an error, propagate the secondary status.
|
|
//
|
|
|
|
if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {
|
|
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
goto LookupSidsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbEnumerateSids(
|
|
IN LSAPR_HANDLE ContainerHandle,
|
|
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
|
|
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
|
OUT PLSAP_DB_SID_ENUMERATION_BUFFER DbEnumerationBuffer,
|
|
IN ULONG PreferedMaximumLength
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
ObjectTypeId - Type of object to be enumerated. The type must be one
|
|
for which all objects have Sids.
|
|
|
|
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.
|
|
|
|
PreferedMaximumLength - prefered maximum length of returned data (in 8-bit
|
|
bytes). This is not a hard upper limit, but serves as a guide. Due to
|
|
data conversion between systems with different natural data sizes, the
|
|
actual amount of data returned may be greater than this value.
|
|
|
|
CountReturned - 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;
|
|
LSAP_DB_ENUMERATION_ELEMENT LastElement;
|
|
PLSAP_DB_ENUMERATION_ELEMENT FirstElement, NextElement, FreeElement;
|
|
ULONG DataLengthUsed;
|
|
ULONG ThisBufferLength;
|
|
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) ||
|
|
!ARGUMENT_PRESENT(EnumerationContext ) ) {
|
|
|
|
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 = *EnumerationContext;
|
|
|
|
for(DataLengthUsed = 0, EntriesRead = 0;
|
|
DataLengthUsed < PreferedMaximumLength;
|
|
DataLengthUsed += ThisBufferLength, EntriesRead++) {
|
|
|
|
//
|
|
// If the absolute maximum length has been exceeded, back off
|
|
// the last object enumerated.
|
|
//
|
|
|
|
if ((DataLengthUsed > LSA_MAXIMUM_ENUMERATION_LENGTH) &&
|
|
(!TrustedClient)) {
|
|
|
|
FirstElement = NextElement->Next;
|
|
MIDL_user_free(NextElement);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate memory for next enumeration element. Set the Sid
|
|
// field to NULL for cleanup purposes.
|
|
//
|
|
|
|
NextElement = MIDL_user_allocate(sizeof (LSAP_DB_ENUMERATION_ELEMENT));
|
|
|
|
if (NextElement == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// 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 = LsapDbFindNextSid(
|
|
ContainerHandle,
|
|
&EnumerationIndex,
|
|
ObjectTypeId,
|
|
(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) {
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// Get the length of the data allocated for the object's Sid
|
|
//
|
|
|
|
ThisBufferLength = RtlLengthSid( NextElement->Sid );
|
|
|
|
//
|
|
// Link the object just found to the front of the enumeration list
|
|
//
|
|
|
|
NextElement->Next = FirstElement;
|
|
FirstElement = NextElement;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
*EnumerationContext = EnumerationIndex;
|
|
|
|
return(Status);
|
|
|
|
EnumerateSidsError:
|
|
|
|
//
|
|
// If necessary, free memory allocated for returning the Sids.
|
|
//
|
|
|
|
if (Sids != NULL) {
|
|
|
|
MIDL_user_free( Sids );
|
|
Sids = NULL;
|
|
}
|
|
|
|
goto EnumerateSidsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbFindNextSid(
|
|
IN LSAPR_HANDLE ContainerHandle,
|
|
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
|
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
|
|
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.
|
|
|
|
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.
|
|
|
|
ObjectTypeId - Type of the objects whose Sids are being enumerated.
|
|
|
|
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;
|
|
UNICODE_STRING SubKeyNameU;
|
|
UNICODE_STRING SidKeyNameU;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE ContDirKeyHandle = NULL;
|
|
HANDLE SidKeyHandle = NULL;
|
|
PSID ObjectSid = NULL;
|
|
|
|
//
|
|
// Zeroise pointers for cleanup routine
|
|
//
|
|
|
|
SidKeyNameU.Buffer = NULL;
|
|
SubKeyNameU.Buffer = NULL;
|
|
|
|
//
|
|
// Setup object attributes for opening the appropriate Containing
|
|
// Directory. For example, if 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[ObjectTypeId],
|
|
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;
|
|
}
|
|
|
|
//
|
|
// 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:
|
|
|
|
//
|
|
// 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
|
|
LsapDbLookupIsolatedWellKnownSids(
|
|
IN ULONG Count,
|
|
IN PLSAPR_SID *Sids,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to identify Sids as Isolated Well-Known Sids
|
|
(Well-Known Sids that do not belong to a domain) and translate them to
|
|
names. Note that Domain Sids for the Well Known domains themselves
|
|
(e.g the Sid of the Built-in Domain) will be identified.
|
|
|
|
WARNING: This function allocates memory for translated names. The
|
|
caller is responsible for freeing this memory after it is no longer
|
|
required.
|
|
|
|
Arguments:
|
|
|
|
Count - Specifies the count of Sids provided in the array Sids.
|
|
|
|
Sids - Pointer to an array of Sids to be examined.
|
|
|
|
TranslatedNames - Pointer to structure that will be initialized to
|
|
references an array of Name translations for the Sids.
|
|
|
|
ReferencedDomains - Pointer to a structure that will be initialized to
|
|
reference a list of the domains used for the translation.
|
|
|
|
The entries in this structure are referenced by the
|
|
structure returned via the Names parameter. Unlike the Names
|
|
parameter, which contains an array entry for (each translated name,
|
|
this structure will only contain one component for each domain
|
|
utilized in the translation.
|
|
|
|
If the specified location contains NULL, a structure will be allocated
|
|
via MIDL_user_allocate.
|
|
|
|
MappedCount - Pointer to location that contains on entry, the number
|
|
of Sids in Sids that have been translated so far. This number
|
|
is updated if any further Sids are translated by this call.
|
|
|
|
CompletelyUnmappedCount - Pointer to location containing the
|
|
count of completely unmapped Sids. A Sid is completely unmapped
|
|
if it is unknown and also its Domain Prefix Sid is not recognized.
|
|
This count is updated on exit, the number of completely unmapped
|
|
Sids whose Domain Prefices are identified by this routine being
|
|
subtracted from the input value.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Sids may remain partially or completely unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
such as memory to complete the call.
|
|
|
|
STATUS_INVALID_PARAMETER - Invalid parameter or parameter combination.
|
|
- *MappedCount > Count
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG SidNumber;
|
|
ULONG UnmappedSidsRemaining;
|
|
PLSAPR_TRANSLATED_NAME OutputNames = NULL;
|
|
ULONG PrefixSidLength;
|
|
UCHAR SubAuthorityCount;
|
|
LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex;
|
|
LSAPR_TRUST_INFORMATION TrustInformation;
|
|
PLSAPR_SID Sid = NULL;
|
|
PLSAPR_SID PrefixSid = NULL;
|
|
LSAP_MM_FREE_LIST FreeList;
|
|
ULONG CleanupFreeListOptions = (ULONG) 0;
|
|
|
|
OutputNames = TranslatedNames->Names;
|
|
|
|
UnmappedSidsRemaining = Count;
|
|
|
|
//
|
|
// Initialize the Free List. We need potentially one entry per Sid,
|
|
// for its Well Known Name (if any).
|
|
//
|
|
|
|
Status = LsapMmCreateFreeList(&FreeList, UnmappedSidsRemaining);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupIsolatedWellKnownSidsError;
|
|
}
|
|
|
|
//
|
|
// Attempt to identify Sids as Well Known Isolated Sids
|
|
//
|
|
|
|
for (SidNumber = 0; SidNumber < Count; SidNumber++) {
|
|
|
|
Sid = Sids[SidNumber];
|
|
|
|
//
|
|
// Attempt to identify the next Sid using the Well Known Sids table,
|
|
// excluding those Sids that are also in the Built In domain. For
|
|
// those, we drop through to the Built in Domain search. Note
|
|
// that only one of these, the Administrators alias is currently
|
|
// present in the table.
|
|
//
|
|
|
|
if (LsapDbLookupIndexWellKnownSid( Sid, &WellKnownSidIndex ) &&
|
|
(WellKnownSidIndex != LsapAliasAdminsSidIndex)) {
|
|
|
|
//
|
|
// Sid is identified. Copy its Well Known Name field
|
|
// UNICODE_STRING structure. Note that not all Well Known
|
|
// Sids have Well Known Names. For those Sids without a
|
|
// Well Known Name, this UNICODE_STRING structure specifies
|
|
// the NULL string.
|
|
//
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
&FreeList,
|
|
(PUNICODE_STRING) &(OutputNames[SidNumber].Name),
|
|
LsapDbWellKnownSidName(WellKnownSidIndex)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the Sid's Use.
|
|
//
|
|
|
|
OutputNames[SidNumber].Use = LsapDbWellKnownSidNameUse(WellKnownSidIndex);
|
|
|
|
PrefixSid = NULL;
|
|
|
|
//
|
|
// If the Sid is a Domain Sid, store pointer to
|
|
// it in the Trust Information.
|
|
//
|
|
|
|
if (OutputNames[SidNumber].Use == SidTypeDomain) {
|
|
|
|
TrustInformation.Sid = Sid;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The Sid is not a domain Sid. Construct the
|
|
// Prefix Sid. This is equal to the original Sid
|
|
// excluding the lowest subauthority (Relative Id).
|
|
//
|
|
|
|
SubAuthorityCount = *RtlSubAuthorityCountSid((PSID) Sid);
|
|
|
|
PrefixSidLength = RtlLengthRequiredSid(SubAuthorityCount - 1);
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
PrefixSid = MIDL_user_allocate( PrefixSidLength );
|
|
|
|
if (PrefixSid == NULL) {
|
|
|
|
break;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
RtlCopyMemory( PrefixSid, Sid, PrefixSidLength );
|
|
|
|
(*RtlSubAuthorityCountSid( (PSID) PrefixSid ))--;
|
|
|
|
TrustInformation.Sid = PrefixSid;
|
|
}
|
|
|
|
//
|
|
// Lookup this Domain Sid or Prefix Sid in the Referenced Domain
|
|
// List. If it is already there, return the DomainIndex for the
|
|
// existing entry and free up the memory allocated for the
|
|
// Prefix Sid (if any).
|
|
//
|
|
|
|
if (LsapDbLookupListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation.Sid,
|
|
(PLONG) &(OutputNames[SidNumber].DomainIndex)
|
|
)) {
|
|
|
|
if ((OutputNames[SidNumber].Use == SidTypeDomain) ||
|
|
(OutputNames[SidNumber].Name.Buffer != NULL)) {
|
|
|
|
UnmappedSidsRemaining--;
|
|
}
|
|
|
|
if (PrefixSid != NULL) {
|
|
|
|
MIDL_user_free(PrefixSid);
|
|
PrefixSid = NULL;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// This Domain or Prefix Sid is not currently on the
|
|
// Referenced Domain List. Complete a Trust Information
|
|
// entry and add it to the List. Copy in the Domain Name
|
|
// (Domain Sids) or NULL string. Note that we use
|
|
// RtlCopyMemory to copy a UNICODE_STRING structure onto
|
|
// a LSAPR_UNICODE_STRING structure.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
&TrustInformation.Name,
|
|
&WellKnownSids[WellKnownSidIndex].DomainName,
|
|
sizeof(UNICODE_STRING)
|
|
);
|
|
|
|
//
|
|
// If the Sid has been recognized as a Well Known Sid and
|
|
// is either a Domain Sid or has a well-known name, count
|
|
// it as being mapped and add the Built-in Domain to the
|
|
// Referenced Domain List.
|
|
//
|
|
|
|
if ((OutputNames[SidNumber].Use == SidTypeDomain) ||
|
|
(OutputNames[SidNumber].Name.Length != 0)) {
|
|
|
|
UnmappedSidsRemaining--;
|
|
|
|
//
|
|
// Make an entry in the list of Referenced Domains. Note
|
|
// that in the case of well-known Sids, the Prefix Sid
|
|
// may or may not be the Sid of a Domain. For those well
|
|
// known Sids whose prefix Sid is not a domain Sid, the
|
|
// Name field in the Trust Information has been set to the
|
|
// NULL string.
|
|
//
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
ReferencedDomains,
|
|
&TrustInformation,
|
|
(PLONG) &OutputNames[SidNumber].DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The Sid is recognized as a Well Known Sid, but is
|
|
// not a Domain Sid and does not have a Well Known Name
|
|
// (signified by a zero length name string). Filter this
|
|
// out.
|
|
//
|
|
|
|
OutputNames[SidNumber].Use = SidTypeUnknown;
|
|
OutputNames[SidNumber].Name.Length = (USHORT) 0;
|
|
OutputNames[SidNumber].Name.MaximumLength = (USHORT) 0;
|
|
OutputNames[SidNumber].Name.Buffer = NULL;
|
|
}
|
|
|
|
//
|
|
// If memory was allocated for a Prefix Sid, free it. Note that
|
|
// the LsapDbLookupAddListTrustedDomains routine will have made
|
|
// a copy of the Sid.
|
|
//
|
|
|
|
if (PrefixSid != NULL) {
|
|
|
|
MIDL_user_free(PrefixSid);
|
|
PrefixSid = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
goto LookupIsolatedWellKnownSidsError;
|
|
}
|
|
|
|
LookupIsolatedWellKnownSidsFinish:
|
|
|
|
//
|
|
// If there is a final PrefixSid buffer, free it.
|
|
//
|
|
|
|
if (PrefixSid != NULL) {
|
|
|
|
MIDL_user_free(PrefixSid);
|
|
PrefixSid = NULL;
|
|
}
|
|
|
|
//
|
|
// Delete the Free List, freeing buffers on the list if an error
|
|
// occurred.
|
|
//
|
|
|
|
LsapMmCleanupFreeList( &FreeList, CleanupFreeListOptions );
|
|
|
|
//
|
|
// Return output parameters.
|
|
//
|
|
|
|
*MappedCount = Count - UnmappedSidsRemaining;
|
|
*CompletelyUnmappedCount = UnmappedSidsRemaining;
|
|
return(Status);
|
|
|
|
LookupIsolatedWellKnownSidsError:
|
|
|
|
//
|
|
// Specify that buffers on Free List are to be freed.
|
|
//
|
|
|
|
CleanupFreeListOptions |= LSAP_MM_FREE_BUFFERS;
|
|
goto LookupIsolatedWellKnownSidsFinish;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
LsapDbLookupIndexWellKnownSid(
|
|
IN PLSAPR_SID Sid,
|
|
OUT PLSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function looks up a Sid to determine if it is well-known. If so,
|
|
an index into the table of well-known Sids is returned.
|
|
|
|
Arguments:
|
|
|
|
Sid - Pointer to Sid to be looked up.
|
|
|
|
WellKnownSidIndex - Pointer to variable that will receive the
|
|
index of the Sid if well known.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the Sid is well-known, else FALSE
|
|
|
|
--*/
|
|
|
|
{
|
|
LSAP_WELL_KNOWN_SID_INDEX Index;
|
|
|
|
//
|
|
// Scan the table of well-known Sids looking for a match.
|
|
//
|
|
|
|
for(Index = LsapWorldSidIndex; Index<LsapDummyLastSidIndex; Index++) {
|
|
|
|
//
|
|
// Allow NULL entries in the table of well-known Sids for now.
|
|
//
|
|
|
|
if (WellKnownSids[Index].Sid == NULL) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If a match is found, return the index to the caller.
|
|
//
|
|
|
|
if (RtlEqualSid((PSID) Sid, WellKnownSids[Index].Sid)) {
|
|
|
|
*WellKnownSidIndex = Index;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The Sid is not a well-known Sid. Return FALSE.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
ULONG LsapDbGetSizeTextSid(
|
|
IN PSID Sid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function computes the size of ASCIIZ buffer required for a
|
|
Sid in character form. Temporarily, the size returned is an over-
|
|
estimate, because 9 digits are allowed for the decimal equivalent
|
|
of each 32-bit SubAuthority value.
|
|
|
|
Arguments:
|
|
|
|
Sid - Pointer to Sid to be sized
|
|
|
|
Return Value:
|
|
|
|
ULONG - The required size of buffer is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG TextSidSize = 0;
|
|
|
|
//
|
|
// Count the Sid prefix and revision "S-rev-". The revision is
|
|
// assumed to not exceed 2 digits.
|
|
//
|
|
|
|
TextSidSize = sizeof("S-nn-");
|
|
|
|
//
|
|
// Add in the size of the identifier authority
|
|
//
|
|
|
|
TextSidSize += 15; // log base 10 of 48 (= 6-byte number)
|
|
|
|
//
|
|
// If the Sid has SubAuthorities, count 9 bytes for each one
|
|
//
|
|
|
|
if (((PLSAPR_SID) Sid)->SubAuthorityCount > 0) {
|
|
|
|
TextSidSize += (ULONG)
|
|
(9 * ((PLSAPR_SID) Sid)->SubAuthorityCount);
|
|
}
|
|
|
|
return TextSidSize;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbSidToTextSid(
|
|
IN PSID Sid,
|
|
OUT PSZ TextSid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function converts a Sid to character text and places it in the
|
|
supplied buffer. The buffer is assumed to be of sufficient size, as
|
|
can be computed by calling LsapDbGetSizeTextSid().
|
|
|
|
Arguments:
|
|
|
|
Sid - Pointer to Sid to be converted.
|
|
|
|
TextSid - Optional pointer to the buffer in which the converted
|
|
Sid will be placed as an ASCIIZ. A NULL pointer ma
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
PLSAPR_SID ISid = Sid;
|
|
ULONG Index;
|
|
ULONG IdentifierAuthorityValue;
|
|
UCHAR Buffer[LSAP_MAX_SIZE_TEXT_SID];
|
|
|
|
sprintf(Buffer, "S-%u-", (USHORT) ISid->Revision );
|
|
strcpy(TextSid, Buffer);
|
|
|
|
if ((ISid->IdentifierAuthority.Value[0] != 0) ||
|
|
(ISid->IdentifierAuthority.Value[1] != 0)) {
|
|
|
|
sprintf(Buffer, "0x%02hx%02hx%02hx%02hx%02hx%02hx",
|
|
(USHORT)ISid->IdentifierAuthority.Value[0],
|
|
(USHORT)ISid->IdentifierAuthority.Value[1],
|
|
(USHORT)ISid->IdentifierAuthority.Value[2],
|
|
(USHORT)ISid->IdentifierAuthority.Value[3],
|
|
(USHORT)ISid->IdentifierAuthority.Value[4],
|
|
(USHORT)ISid->IdentifierAuthority.Value[5] );
|
|
strcat(TextSid, Buffer);
|
|
|
|
} else {
|
|
|
|
IdentifierAuthorityValue =
|
|
(ULONG)ISid->IdentifierAuthority.Value[5] +
|
|
(ULONG)(ISid->IdentifierAuthority.Value[4] << 8) +
|
|
(ULONG)(ISid->IdentifierAuthority.Value[3] << 16) +
|
|
(ULONG)(ISid->IdentifierAuthority.Value[2] << 24);
|
|
sprintf(Buffer, "%lu", IdentifierAuthorityValue);
|
|
strcat(TextSid, Buffer);
|
|
}
|
|
|
|
//
|
|
// Now format the Sub Authorities (if any) as text.
|
|
//
|
|
|
|
for (Index = 0; Index < (ULONG) ISid->SubAuthorityCount; Index++ ) {
|
|
|
|
sprintf(Buffer, "-%lu", ISid->SubAuthority[Index]);
|
|
strcat(TextSid, Buffer);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbSidToUnicodeSid(
|
|
IN PSID Sid,
|
|
OUT PUNICODE_STRING SidU,
|
|
IN BOOLEAN AllocateDestinationString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function converts a Sid to Unicode form and optionally allocates
|
|
(via MIDL_user_allocate) memory for the string buffer.
|
|
|
|
Arguments:
|
|
|
|
Sid - Pointer to Sid to be translated.
|
|
|
|
SidU - Pointer to Unicode string that will receive the Unicode
|
|
Sid text.
|
|
|
|
AllocateDestinationString - If TRUE, the buffer for the destination
|
|
string will be allocated. If FALSE, it is assummed that the
|
|
destination Unicode string references a buffer of sufficient size.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG TextSidSize;
|
|
PSZ TextSid;
|
|
ANSI_STRING SidAnsi;
|
|
|
|
//
|
|
// First, query the amount of memory required for a buffer that
|
|
// will hold the Sid as an ASCIIZ character string.
|
|
//
|
|
|
|
TextSidSize = LsapDbGetSizeTextSid(Sid);
|
|
|
|
//
|
|
// Now allocate a buffer for the Text Sid.
|
|
//
|
|
|
|
TextSid = LsapAllocateLsaHeap(TextSidSize);
|
|
|
|
if (TextSid == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Convert the Sid to ASCIIZ and place in the buffer.
|
|
//
|
|
|
|
Status = LsapDbSidToTextSid(Sid, TextSid);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
LsapFreeLsaHeap(TextSid);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Now convert the text Sid to Unicode form via ANSI string form.
|
|
// If we are to allocate the output buffer, do so via the
|
|
// midl_USER_allocate routine.
|
|
//
|
|
|
|
RtlInitString(&SidAnsi, TextSid);
|
|
|
|
if (AllocateDestinationString) {
|
|
|
|
SidU->MaximumLength = (USHORT) RtlAnsiStringToUnicodeSize(&SidAnsi);
|
|
SidU->Buffer = MIDL_user_allocate( SidU->MaximumLength );
|
|
SidU->Length = 0;
|
|
}
|
|
|
|
//
|
|
// Now convert the Ansi String to a Unicode string. The buffer is
|
|
// already allocated. Free Text Sid buffer before checking conversion
|
|
// status.
|
|
//
|
|
|
|
Status = RtlAnsiStringToUnicodeString(SidU, &SidAnsi, FALSE);
|
|
LsapFreeLsaHeap(TextSid);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (AllocateDestinationString) {
|
|
|
|
MIDL_user_free(SidU->Buffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupTranslateUnknownSids(
|
|
IN ULONG Count,
|
|
IN PLSAPR_SID *Sids,
|
|
IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
|
|
IN ULONG MappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function translates unmapped Sids to a character representation.
|
|
If the Domain of a Sid is unknown, the entire Sid is translated,
|
|
otherwise the Relative Id only is translated.
|
|
|
|
Parameters:
|
|
|
|
Count - Specifies the number of Sids in the array.
|
|
|
|
Sids - Pointer to an array of Sids. Some of these will already
|
|
have been translated.
|
|
|
|
ReferencedDomains - Pointer to Referenced Domains List header.
|
|
|
|
TranslatedNames - Pointer to structure that references the array of
|
|
translated names. The nth element of the referenced array
|
|
corresponds to the nth Sid in the Sids array. Some of the
|
|
Sids may be already translated and will be ignored. Those that are
|
|
not yet translated have zero length Unicode structures with NULL
|
|
buffer pointers. Already translated Sids are ignored.
|
|
|
|
MappedCount - Specifies the number of Sids that have already been
|
|
translated.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ULONG SidIndex;
|
|
ULONG UnmappedCount;
|
|
PSID Sid;
|
|
UNICODE_STRING NameU;
|
|
PLSA_TRANSLATED_NAME Names = (PLSA_TRANSLATED_NAME) TranslatedNames->Names;
|
|
LSAP_MM_FREE_LIST FreeList;
|
|
ULONG CleanupFreeListOptions = (ULONG) 0;
|
|
UnmappedCount = Count - MappedCount;
|
|
|
|
//
|
|
// Create FreeList
|
|
//
|
|
|
|
Status = LsapMmCreateFreeList(&FreeList, UnmappedCount);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto TranslateUnknownSidsError;
|
|
}
|
|
|
|
//
|
|
// Examine the array of Sids, looking for Unknown ones to translate.
|
|
// Translate any Unknown ones found to character representations,
|
|
// and stop either when all of them have been accounted for, or when
|
|
// the end of the array is reached.
|
|
//
|
|
|
|
if (MappedCount == Count) {
|
|
|
|
goto TranslateUnknownSidsFinish;
|
|
}
|
|
|
|
if (MappedCount > Count) {
|
|
|
|
goto TranslateUnknownSidsError;
|
|
}
|
|
|
|
for (SidIndex = 0, UnmappedCount = Count - MappedCount;
|
|
(SidIndex < Count) && (UnmappedCount > 0);
|
|
SidIndex++) {
|
|
|
|
Sid = Sids[SidIndex];
|
|
|
|
//
|
|
// If the Sid has already been mapped, ignore it.
|
|
//
|
|
|
|
if (Names[SidIndex].Use != SidTypeUnknown) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Found an unmapped Sid. If the domain is known, convert the
|
|
// Relative Id of the Sid to a Unicode String, limited to 8
|
|
// characters and with leading zeros.
|
|
//
|
|
|
|
if (Names[SidIndex].DomainIndex >= 0) {
|
|
|
|
//
|
|
// Convert the Relative Id to a Unicode Name and store in
|
|
// the Translation.
|
|
//
|
|
|
|
Status = LsapRtlSidToUnicodeRid( Sid, &NameU );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto TranslateUnknownSidsError;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The Domain is unknown. In this case, convert the whole Sid
|
|
// to the standard character representation.
|
|
//
|
|
|
|
Status = LsapRtlConvertSidToUnicodeString( Sid, &NameU );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto TranslateUnknownSidsError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the Unicode Name to the output, allocating memory for
|
|
// its buffer via MIDL_user_allocate
|
|
//
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
&FreeList,
|
|
&Names[SidIndex].Name,
|
|
&NameU
|
|
);
|
|
|
|
RtlFreeUnicodeString(&NameU);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto TranslateUnknownSidsError;
|
|
}
|
|
|
|
//
|
|
// Decrement the remaining Unmapped Count
|
|
//
|
|
|
|
UnmappedCount--;
|
|
}
|
|
|
|
TranslateUnknownSidsFinish:
|
|
|
|
//
|
|
// Delete the Free List, freeing buffers on the list if an error
|
|
// occurred.
|
|
//
|
|
|
|
LsapMmCleanupFreeList(&FreeList, CleanupFreeListOptions);
|
|
return(Status);
|
|
|
|
TranslateUnknownSidsError:
|
|
|
|
//
|
|
// Specify that buffers on Free List are to be freed.
|
|
//
|
|
|
|
CleanupFreeListOptions |= LSAP_MM_FREE_BUFFERS;
|
|
goto TranslateUnknownSidsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsarLookupNames(
|
|
IN LSAPR_HANDLE PolicyHandle,
|
|
IN ULONG Count,
|
|
IN PLSAPR_UNICODE_STRING Names,
|
|
OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the LSA server worker routine for the LsaLookupNames
|
|
API.
|
|
|
|
The LsaLookupNames API attempts to translate names of domains, users,
|
|
groups or aliases to Sids. The caller must have POLICY_LOOKUP_NAMES
|
|
access to the Policy object.
|
|
|
|
Names may be either isolated (e.g. JohnH) or composite names containing
|
|
both the domain name and account name. Composite names must include a
|
|
backslash character separating the domain name from the account name
|
|
(e.g. Acctg\JohnH). An isolated name may be either an account name
|
|
(user, group, or alias) or a domain name.
|
|
|
|
Translation of isolated names introduces the possibility of name
|
|
collisions (since the same name may be used in multiple domains). An
|
|
isolated name will be translated using the following algorithm:
|
|
|
|
If the name is a well-known name (e.g. Local or Interactive), then the
|
|
corresponding well-known Sid is returned.
|
|
|
|
If the name is the Built-in Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Account Domain's name, then that domain's Sid
|
|
will be returned.
|
|
/
|
|
If the name is the Primary Domain's name, then that domain's Sid will
|
|
be returned.
|
|
|
|
If the name is a user, group, or alias in the Built-in Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
If the name is a user, group, or alias in the Primary Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
Otherwise, the name is not translated.
|
|
|
|
NOTE: Proxy, Machine, and Trust user accounts are not referenced
|
|
for name translation. Only normal user accounts are used for ID
|
|
translation. If translation of other account types is needed, then
|
|
SAM services should be used directly.
|
|
|
|
Arguments:
|
|
|
|
This function is the LSA server RPC worker routine for the
|
|
LsaLookupNames API.
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
Count - Specifies the number of names to be translated.
|
|
|
|
Names - Pointer to an array of Count Unicode String structures
|
|
specifying the names to be looked up and mapped to Sids.
|
|
The strings may be names of User, Group or Alias accounts or
|
|
domains.
|
|
|
|
ReferencedDomains - Receives a pointer to a structure describing the
|
|
domains used for the translation. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for
|
|
each translated name, this structure will only contain one
|
|
component for each domain utilized in the translation.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
TranslatedSids - Pointer to a structure which will (or already) references an array of
|
|
records describing each translated Sid. The nth entry in this array
|
|
provides a translation for the nth element in the Names parameter.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on this
|
|
machine. Values of this field are are follows:
|
|
|
|
LsapLookupWksta - First Level Lookup performed on a workstation
|
|
normally configured for Windows-Nt. The lookup searches the
|
|
Well-Known Sids/Names, and the Built-in Domain and Account Domain
|
|
in the local SAM Database. If not all Sids or Names are
|
|
identified, performs a "handoff" of a Second level Lookup to the
|
|
LSA running on a Controller for the workstation's Primary Domain
|
|
(if any).
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Sids or Names are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
MappedCount - Pointer to location that contains a count of the Names
|
|
mapped so far. On exit, this will be updated.
|
|
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully and all Names have
|
|
been translated to Sids.
|
|
|
|
STATUS_SOME_NOT_MAPPED - At least one of the names provided was
|
|
trasnlated to a Sid, but not all names could be translated. This
|
|
is a success status.
|
|
|
|
STATUS_NONE_MAPPED - None of the names provided could be translated
|
|
to Sids. This is an error status, but output is returned. Such
|
|
output includes partial translations of names whose domain could
|
|
be identified.
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, SecondaryStatus;
|
|
BOOLEAN PolicyHandleReferencedHere = FALSE;
|
|
LSAPR_HANDLE TrustedDomainHandle = NULL;
|
|
PPOLICY_LSA_SERVER_ROLE PolicyLsaServerRoleInfo = NULL;
|
|
PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
|
|
LSA_HANDLE ControllerPolicyHandle = NULL;
|
|
ULONG DomainIndex;
|
|
LSAPR_TRUST_INFORMATION TrustInformation;
|
|
LSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation;
|
|
LSAPR_TRUST_INFORMATION AccountDomainTrustInformation;
|
|
LSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation;
|
|
ULONG NullNameCount = 0;
|
|
ULONG NameIndex;
|
|
PLSA_TRANSLATED_SID OutputSids;
|
|
PLSAPR_TRUST_INFORMATION Domains = NULL;
|
|
ULONG OutputSidsLength;
|
|
ULONG CompletelyUnmappedCount = Count;
|
|
ULONG IsolatedNameCount;
|
|
ULONG MappedIsolatedNameCount = (ULONG) 0;
|
|
ULONG LocalDomainsToSearch = 0;
|
|
|
|
PLSAPR_UNICODE_STRING PrefixNames = NULL;
|
|
PLSAPR_UNICODE_STRING SuffixNames = NULL;
|
|
LSAPR_UNICODE_STRING BackSlash;
|
|
|
|
BuiltInDomainTrustInformation.Name.Buffer = NULL;
|
|
BuiltInDomainTrustInformation.Sid = NULL;
|
|
|
|
AccountDomainTrustInformation.Name.Buffer = NULL;
|
|
AccountDomainTrustInformation.Sid = NULL;
|
|
|
|
PrimaryDomainTrustInformation.Name.Buffer = NULL;
|
|
PrimaryDomainTrustInformation.Sid = NULL;
|
|
|
|
//
|
|
// If there are no completely unmapped Names remaining, return.
|
|
//
|
|
|
|
if (CompletelyUnmappedCount == (ULONG) 0) {
|
|
|
|
goto LookupNamesFinish;
|
|
}
|
|
|
|
//
|
|
// Names provided are either Isolated, consisting of a single
|
|
// component, or composite, having the form
|
|
//
|
|
// <DomainName>\<SuffixName>
|
|
//
|
|
// Split the list of names into two separate arrays, one containing
|
|
// the Domain Prefixes (or NULL strings) and the other array
|
|
// containing the Terminal Names. Both arrays are the same size
|
|
// as the original. First, allocate memory for the output arrays
|
|
// of UNICODE_STRING structures.
|
|
//
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
PrefixNames = MIDL_user_allocate( Count * sizeof( UNICODE_STRING ));
|
|
|
|
if (PrefixNames == NULL) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
SuffixNames = MIDL_user_allocate( Count * sizeof( UNICODE_STRING ));
|
|
|
|
if (SuffixNames == NULL) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
RtlInitUnicodeString( (PUNICODE_STRING) &BackSlash, L"\\" );
|
|
|
|
LsapRtlSplitNames(
|
|
(PUNICODE_STRING) Names,
|
|
Count,
|
|
(PUNICODE_STRING) &BackSlash,
|
|
(PUNICODE_STRING) PrefixNames,
|
|
(PUNICODE_STRING) SuffixNames
|
|
);
|
|
|
|
//
|
|
// Count the Isolated Names.
|
|
//
|
|
|
|
IsolatedNameCount = (ULONG) 0;
|
|
|
|
for (NameIndex = 0; NameIndex < Count; NameIndex++) {
|
|
|
|
if (PrefixNames[NameIndex].Length == (USHORT) 0) {
|
|
|
|
IsolatedNameCount++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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 he handle.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
PolicyHandle,
|
|
POLICY_LOOKUP_NAMES,
|
|
PolicyObject,
|
|
LSAP_DB_ACQUIRE_LOCK
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
//
|
|
// We can dereference the original PolicyHandle and release the lock on
|
|
// the LSA Database; if we need to access the database again, we'll
|
|
// use the trusted Lsa handle and the appropriate API will take
|
|
// the lock as required.
|
|
//
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&PolicyHandle,
|
|
PolicyObject,
|
|
LSAP_DB_RELEASE_LOCK,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
TranslatedSids->Sids = NULL;
|
|
TranslatedSids->Entries = 0;
|
|
*ReferencedDomains = NULL;
|
|
|
|
//
|
|
// Allocate Output Sids array buffer.
|
|
//
|
|
|
|
OutputSidsLength = Count * sizeof(LSA_TRANSLATED_SID);
|
|
OutputSids = MIDL_user_allocate(OutputSidsLength);
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (OutputSids == NULL) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
TranslatedSids->Entries = Count;
|
|
TranslatedSids->Sids = OutputSids;
|
|
|
|
//
|
|
// Initialize the Output Sids array. Zeroise all fields, then
|
|
// Mark all of the Output Sids as being unknown initially and
|
|
// set the DomainIndex fields to a negative number meaning
|
|
// "no domain"
|
|
//
|
|
|
|
RtlZeroMemory( OutputSids, OutputSidsLength);
|
|
|
|
for (NameIndex = 0; NameIndex < Count; NameIndex++) {
|
|
|
|
OutputSids[NameIndex].Use = SidTypeUnknown;
|
|
OutputSids[NameIndex].DomainIndex = LSA_UNKNOWN_INDEX;
|
|
}
|
|
|
|
//
|
|
// Create an empty Referenced Domain List.
|
|
//
|
|
|
|
Status = LsapDbLookupCreateListReferencedDomains( ReferencedDomains, 0 );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
//
|
|
// Obtain the Trust Information for the
|
|
// Built-in, Account and Primary Domains.
|
|
//
|
|
|
|
Status = LsapDbLookupLocalDomains(
|
|
&BuiltInDomainTrustInformation,
|
|
&AccountDomainTrustInformation,
|
|
&PrimaryDomainTrustInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
//
|
|
// The local domains to be searched always include the Accounts
|
|
// domain. For initial lookup targets only, the BUILT_IN domain is
|
|
// also searched.
|
|
//
|
|
|
|
LocalDomainsToSearch = LSAP_DB_SEARCH_ACCOUNT_DOMAIN;
|
|
|
|
if (LookupLevel == LsapLookupWksta) {
|
|
|
|
LocalDomainsToSearch |= LSAP_DB_SEARCH_BUILT_IN_DOMAIN;
|
|
|
|
//
|
|
// This is the lowest Lookup Level, normally targeted at a
|
|
// Workstation.
|
|
//
|
|
|
|
if (IsolatedNameCount > (ULONG) 0) {
|
|
|
|
Status = LsapDbLookupIsolatedNames(
|
|
Count,
|
|
IsolatedNameCount,
|
|
Names,
|
|
PrefixNames,
|
|
SuffixNames,
|
|
&BuiltInDomainTrustInformation,
|
|
&AccountDomainTrustInformation,
|
|
&PrimaryDomainTrustInformation,
|
|
*ReferencedDomains,
|
|
TranslatedSids,
|
|
MappedCount,
|
|
&CompletelyUnmappedCount,
|
|
&MappedIsolatedNameCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If all Names are now mapped or partially mapped, or only zero
|
|
// length names remain, finish.
|
|
//
|
|
|
|
NullNameCount = 0;
|
|
|
|
for( NameIndex = 0; NameIndex < Count; NameIndex++) {
|
|
|
|
if (Names[NameIndex].Length == 0) {
|
|
|
|
NullNameCount++;
|
|
}
|
|
}
|
|
|
|
if (CompletelyUnmappedCount == NullNameCount) {
|
|
|
|
goto LookupNamesFinish;
|
|
}
|
|
}
|
|
|
|
//
|
|
// There are some remaining unmapped Names. They may belong to a
|
|
// local SAM Domain. Currently, there are two such domains, the
|
|
// Built-in Domain and the Accounts Domain. Search these
|
|
// domains now, excluding the BUILT_IN domain from higher level
|
|
// searches.
|
|
//
|
|
|
|
Status = LsapDbLookupNamesInLocalDomains(
|
|
Count,
|
|
Names,
|
|
PrefixNames,
|
|
SuffixNames,
|
|
&BuiltInDomainTrustInformation,
|
|
&AccountDomainTrustInformation,
|
|
*ReferencedDomains,
|
|
TranslatedSids,
|
|
MappedCount,
|
|
&CompletelyUnmappedCount,
|
|
LocalDomainsToSearch
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
//
|
|
// If all Names apart from NULL names are now mapped, finish.
|
|
//
|
|
|
|
if (CompletelyUnmappedCount == NullNameCount) {
|
|
|
|
goto LookupNamesFinish;
|
|
}
|
|
|
|
//
|
|
// Not all of the Names have been identified in the local domain(s).
|
|
// The next step in the search depends on the level of this lookup
|
|
// and how we are configured as follows:
|
|
//
|
|
// Lookup Level Configuration Lookup search next
|
|
//
|
|
// LsapLookupWksta Win Nt Primary Domain
|
|
// LanMan Nt Trusted Domains
|
|
//
|
|
// LsapLookupPDC Win Nt error
|
|
// LanMan Nt Trusted Domains
|
|
//
|
|
// LsaLookupTDL Win Nt error
|
|
// LanMan Nt none
|
|
//
|
|
|
|
if (LookupLevel == LsapLookupWksta) {
|
|
|
|
if (LsapProductType != NtProductLanManNt) {
|
|
|
|
//
|
|
// If there is no Primary Domain as in the case of a WORKGROUP,
|
|
// just finish up. Set a default result code STATUS_SUCCESS.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (PrimaryDomainTrustInformation.Sid == NULL) {
|
|
|
|
goto LookupNamesFinish;
|
|
}
|
|
|
|
//
|
|
// There is a Primary Domain. Search it for Names. Since a
|
|
// Primary Domain is also a Trusted Domain, we use the
|
|
// Trusted Domain search routine. This routine will "hand off"
|
|
// the search to a Domain Controller's LSA.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
&TrustInformation.Name,
|
|
&PrimaryDomainTrustInformation.Name,
|
|
sizeof( UNICODE_STRING)
|
|
);
|
|
|
|
TrustInformation.Sid = (PSID) PrimaryDomainTrustInformation.Sid;
|
|
|
|
Status = LsapDbLookupNamesInPrimaryDomain(
|
|
Count,
|
|
Names,
|
|
PrefixNames,
|
|
SuffixNames,
|
|
&TrustInformation,
|
|
*ReferencedDomains,
|
|
TranslatedSids,
|
|
LsapLookupPDC,
|
|
MappedCount,
|
|
&CompletelyUnmappedCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
goto LookupNamesFinish;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We reach here in two cases:
|
|
//
|
|
// * Initial Level lookups targeted at DC's
|
|
// * Higher Level Lookups (must be targeted at DC's)
|
|
//
|
|
// For the highest level lookup, that on an individual TDC, there
|
|
// is no more searching to do, since we have already searched the
|
|
// Accounts Domain and we do not follow trust relationships on DC's
|
|
// beyond one level.
|
|
//
|
|
|
|
if (LookupLevel == LsapLookupTDL) {
|
|
|
|
goto LookupNamesFinish;
|
|
}
|
|
|
|
//
|
|
// We are either the initial target of the lookup but not configured
|
|
// as a workstation, or we are the target of a Primary Domain
|
|
// level lookup. In either case, we must be configured as a DC.
|
|
//
|
|
|
|
Status = STATUS_DOMAIN_CTRLR_CONFIG_ERROR;
|
|
|
|
if (LsapProductType != NtProductLanManNt) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
//
|
|
// Search all of the Trusted Domains
|
|
//
|
|
|
|
Status = LsapDbLookupNamesInTrustedDomains(
|
|
Count,
|
|
Names,
|
|
PrefixNames,
|
|
SuffixNames,
|
|
*ReferencedDomains,
|
|
TranslatedSids,
|
|
LsapLookupTDL,
|
|
MappedCount,
|
|
&CompletelyUnmappedCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
|
|
LookupNamesFinish:
|
|
|
|
//
|
|
// If some but not all Names were mapped, return informational status
|
|
// STATUS_SOME_NOT_MAPPED. If no Names were mapped, return error
|
|
// STATUS_NONE_MAPPED. Note that we expect and STATUS_NONE_MAPPED
|
|
// errors returned by called routines to have been suppressed before
|
|
// we get here. The reason for this is that we need to calculate
|
|
// the return Status based on the whole set of Names, not some subset
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (*MappedCount < Count) {
|
|
|
|
Status = STATUS_SOME_NOT_MAPPED;
|
|
|
|
if (*MappedCount == 0) {
|
|
|
|
Status = STATUS_NONE_MAPPED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we opened a handle to the Primary Domain's TrustedDomain
|
|
// object, close it.
|
|
//
|
|
|
|
if (TrustedDomainHandle != NULL) {
|
|
|
|
SecondaryStatus = LsarClose( &TrustedDomainHandle );
|
|
TrustedDomainHandle == NULL;
|
|
|
|
//
|
|
// If the close failed, go to error, returning its status if the
|
|
// primary status was a success code.
|
|
//
|
|
|
|
if (!NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
goto LookupNamesError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Account and
|
|
// Primary Domain Name and Sid buffers. The memory for the Built-In
|
|
// Domain Name and Sid buffers is not freed since these are global
|
|
// data in the Well Known Sid table.
|
|
//
|
|
|
|
if ( AccountDomainTrustInformation.Name.Buffer != NULL ) {
|
|
|
|
MIDL_user_free( AccountDomainTrustInformation.Name.Buffer );
|
|
}
|
|
|
|
if ( AccountDomainTrustInformation.Sid != NULL ) {
|
|
|
|
MIDL_user_free( AccountDomainTrustInformation.Sid );
|
|
}
|
|
|
|
if ( PrimaryDomainTrustInformation.Name.Buffer != NULL ) {
|
|
|
|
MIDL_user_free( PrimaryDomainTrustInformation.Name.Buffer );
|
|
}
|
|
|
|
if ( PrimaryDomainTrustInformation.Sid != NULL ) {
|
|
|
|
MIDL_user_free( PrimaryDomainTrustInformation.Sid );
|
|
}
|
|
|
|
//
|
|
// If necessary, free the arrays of PrefixNames and SuffixNames
|
|
//
|
|
|
|
if (PrefixNames != NULL) {
|
|
|
|
MIDL_user_free(PrefixNames);
|
|
}
|
|
|
|
if (SuffixNames != NULL) {
|
|
|
|
MIDL_user_free(SuffixNames);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupNamesError:
|
|
|
|
//
|
|
// If the LookupLevel is the lowest (Workstation Level) free up
|
|
// the Sids and Referenced Domains arrays.
|
|
//
|
|
|
|
if (LookupLevel == LsapLookupWksta) {
|
|
|
|
//
|
|
// If necessary, free the Sids array.
|
|
//
|
|
|
|
if (TranslatedSids->Sids != NULL) {
|
|
|
|
MIDL_user_free( TranslatedSids->Sids );
|
|
TranslatedSids->Sids = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Referenced Domain List.
|
|
//
|
|
|
|
if (*ReferencedDomains != NULL) {
|
|
|
|
Domains = (*ReferencedDomains)->Domains;
|
|
|
|
if (Domains != NULL) {
|
|
|
|
for (DomainIndex = 0;
|
|
DomainIndex < (*ReferencedDomains)->Entries;
|
|
DomainIndex++) {
|
|
|
|
if (Domains[ DomainIndex ].Name.Buffer != NULL) {
|
|
|
|
MIDL_user_free( Domains[ DomainIndex ].Name.Buffer );
|
|
Domains[ DomainIndex ].Name.Buffer == NULL;
|
|
}
|
|
|
|
if (Domains[ DomainIndex ].Sid != NULL) {
|
|
|
|
MIDL_user_free( Domains[ DomainIndex ].Sid );
|
|
Domains[ DomainIndex ].Sid == NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
MIDL_user_free( *ReferencedDomains );
|
|
*ReferencedDomains = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the primary status was a success code, but the secondary
|
|
// status was an error, propagate the secondary status.
|
|
//
|
|
|
|
if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {
|
|
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
goto LookupNamesFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbEnumerateNames(
|
|
IN LSAPR_HANDLE ContainerHandle,
|
|
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
|
|
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
|
OUT PLSAP_DB_NAME_ENUMERATION_BUFFER DbEnumerationBuffer,
|
|
IN ULONG PreferedMaximumLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function enumerates Names 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.
|
|
|
|
ObjectTypeId - Type of object to be enumerated. The type must be one
|
|
for which all objects have Names.
|
|
|
|
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 Names. These Names may be used together with object type to
|
|
open the objects and obtain any further information available.
|
|
|
|
PreferedMaximumLength - prefered maximum length of returned data (in 8-bit
|
|
bytes). This is not a hard upper limit, but serves as a guide. Due to
|
|
data conversion between systems with different natural data sizes, the
|
|
actual amount of data returned may be greater than this value.
|
|
|
|
CountReturned - 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 there are no more objects to enumerate. Note that
|
|
zero or more objects may be enumerated on a call that returns this
|
|
reply.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_ENUMERATION_ELEMENT LastElement;
|
|
PLSAP_DB_ENUMERATION_ELEMENT FirstElement, NextElement, FreeElement;
|
|
ULONG DataLengthUsed;
|
|
ULONG ThisBufferLength;
|
|
PUNICODE_STRING Names = 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)) ||
|
|
(!ARGUMENT_PRESENT(EnumerationContext )) ) {
|
|
|
|
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 = *EnumerationContext;
|
|
|
|
for(DataLengthUsed = 0, EntriesRead = 0;
|
|
DataLengthUsed < PreferedMaximumLength;
|
|
DataLengthUsed += ThisBufferLength, EntriesRead++) {
|
|
|
|
//
|
|
// If the absolute maximum length has been exceeded, back off
|
|
// the last object enumerated.
|
|
//
|
|
|
|
if ((DataLengthUsed > LSA_MAXIMUM_ENUMERATION_LENGTH) &&
|
|
(!TrustedClient)) {
|
|
|
|
FirstElement = NextElement->Next;
|
|
MIDL_user_free(NextElement);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate memory for next enumeration element. Set the Name
|
|
// field to NULL for cleanup purposes.
|
|
//
|
|
|
|
NextElement = MIDL_user_allocate(sizeof (LSAP_DB_ENUMERATION_ELEMENT));
|
|
|
|
if (NextElement == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Find the next object's Name, 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 = LsapDbFindNextName(
|
|
ContainerHandle,
|
|
&EnumerationIndex,
|
|
ObjectTypeId,
|
|
(PLSAPR_UNICODE_STRING) &NextElement->Name
|
|
);
|
|
|
|
//
|
|
// 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) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the length of the data allocated for the object's Name
|
|
//
|
|
|
|
ThisBufferLength = NextElement->Name.Length;
|
|
|
|
//
|
|
// Link the object just found to the front of the enumeration list
|
|
//
|
|
|
|
NextElement->Next = FirstElement;
|
|
FirstElement = NextElement;
|
|
}
|
|
|
|
//
|
|
// If an error other than STATUS_NO_MORE_ENTRIES occurred, return it.
|
|
//
|
|
|
|
if ((Status != STATUS_NO_MORE_ENTRIES) && !NT_SUCCESS(Status)) {
|
|
|
|
goto EnumerateNamesError;
|
|
}
|
|
|
|
//
|
|
// The enumeration is complete or has terminated because of return
|
|
// buffer limitations. If no entries were read, return.
|
|
//
|
|
|
|
if (EntriesRead != 0) {
|
|
|
|
|
|
//
|
|
// Some entries were read, allocate an information buffer for returning
|
|
// them.
|
|
//
|
|
|
|
Names = MIDL_user_allocate( sizeof (UNICODE_STRING) * EntriesRead );
|
|
|
|
if (Names == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto EnumerateNamesError;
|
|
}
|
|
|
|
//
|
|
// Memory was successfully allocated for the return buffer.
|
|
// Copy in the enumerated Names.
|
|
//
|
|
|
|
for (NextElement = FirstElement, Index = 0;
|
|
NextElement != &LastElement;
|
|
NextElement = NextElement->Next, Index++) {
|
|
|
|
ASSERT(Index < EntriesRead);
|
|
|
|
Names[Index] = NextElement->Name;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// No entries available this call.
|
|
//
|
|
|
|
Status = STATUS_NO_MORE_ENTRIES;
|
|
|
|
}
|
|
|
|
EnumerateNamesFinish:
|
|
|
|
//
|
|
// Free the enumeration element structures (if any).
|
|
//
|
|
|
|
for (NextElement = FirstElement; NextElement != &LastElement;) {
|
|
|
|
//
|
|
// If an error has occurred, dispose of memory allocated
|
|
// for any Names.
|
|
//
|
|
|
|
if (!(NT_SUCCESS(Status) || (Status == STATUS_NO_MORE_ENTRIES))) {
|
|
|
|
if (NextElement->Name.Buffer != NULL) {
|
|
|
|
MIDL_user_free(NextElement->Name.Buffer);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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->Names = Names;
|
|
*EnumerationContext = EnumerationIndex;
|
|
|
|
return(Status);
|
|
|
|
EnumerateNamesError:
|
|
|
|
//
|
|
// If necessary, free memory allocated for returning the Names.
|
|
//
|
|
|
|
if (Names != NULL) {
|
|
|
|
MIDL_user_free( Names );
|
|
Names = NULL;
|
|
}
|
|
|
|
goto EnumerateNamesFinish;
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapDbUpdateCountCompUnmappedNames(
|
|
OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN OUT PULONG CompletelyUnmappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function updates the count of completely unmapped names in a
|
|
name lookup operation. A name is completely unmapped if its domain
|
|
is unknown.
|
|
|
|
Arguments:
|
|
|
|
TranslatedSids - Pointer to a structure which will be initialized to
|
|
reference an array of records describing each translated Sid. The
|
|
nth entry in this array provides a translation for the nth element in
|
|
the Names parameter.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
CompletelyUnmappedCount - Pointer to location that will receive
|
|
a count of completely unmapped Sids. A Name is completely unmapped
|
|
if it is isolated and unknown, or is composite and its Domain Prefix
|
|
component is not recognized as a Domain Name.
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Count = TranslatedSids->Entries;
|
|
ULONG SidIndex;
|
|
ULONG UpdatedCompletelyUnmappedCount = 0;
|
|
|
|
for (SidIndex = 0; SidIndex < Count; SidIndex++) {
|
|
|
|
if (TranslatedSids->Sids[SidIndex].DomainIndex == LSA_UNKNOWN_INDEX) {
|
|
|
|
UpdatedCompletelyUnmappedCount++;
|
|
}
|
|
}
|
|
|
|
ASSERT(UpdatedCompletelyUnmappedCount <= *CompletelyUnmappedCount);
|
|
*CompletelyUnmappedCount = UpdatedCompletelyUnmappedCount;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbFindNextName(
|
|
IN LSAPR_HANDLE ContainerHandle,
|
|
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
|
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
|
|
OUT PLSAPR_UNICODE_STRING NextName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function finds the next Name of object of a given type within a
|
|
container object. The given object type must be one where objects
|
|
have Names. The Names returned can be used on subsequent open calls to
|
|
access the objects.
|
|
|
|
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.
|
|
|
|
ObjectTypeId - Type of the objects whose Names are being enumerated.
|
|
Ccurrently, this is restricted to objects (such as Secret Objects)
|
|
that are accessed by Name only.
|
|
|
|
NextName - Pointer to Unicode String that will be initialized to
|
|
reference the next Name 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 NameKeyValueLength = 0;
|
|
LSAPR_UNICODE_STRING SubKeyNameU;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE ContDirKeyHandle = NULL;
|
|
|
|
|
|
//
|
|
// Setup object attributes for opening the appropriate Containing
|
|
// Directory. For example, if 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[ObjectTypeId],
|
|
OBJ_CASE_INSENSITIVE,
|
|
((LSAP_DB_HANDLE) ContainerHandle)->KeyHandle,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// If the object type is not accessed by Name only, return an error.
|
|
// Currently, only Secret objects have this property.
|
|
//
|
|
|
|
|
|
if (ObjectTypeId != SecretObject) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
Status = RtlpNtOpenKey(
|
|
&ContDirKeyHandle,
|
|
KEY_READ,
|
|
&ObjectAttributes,
|
|
0
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// 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 Name, 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;
|
|
} else {
|
|
|
|
//
|
|
// Now enumerate the next subkey.
|
|
//
|
|
|
|
Status = RtlpNtEnumerateSubKey(
|
|
ContDirKeyHandle,
|
|
(PUNICODE_STRING) &SubKeyNameU,
|
|
*EnumerationContext,
|
|
NULL
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
(*EnumerationContext)++;
|
|
|
|
//
|
|
// Return the Name.
|
|
//
|
|
|
|
*NextName = SubKeyNameU;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Not successful - free the subkey name buffer
|
|
// Note that STATUS_NO_MORE_ENTRIES is a warning
|
|
// (not a success) code.
|
|
//
|
|
|
|
LsapFreeLsaHeap( SubKeyNameU.Buffer );
|
|
|
|
//
|
|
// Set the out parameter so RPC doesn't try
|
|
// to return anything.
|
|
//
|
|
|
|
NextName->Length = NextName->MaximumLength = 0;
|
|
NextName->Buffer = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Close the containing directory handle
|
|
//
|
|
|
|
SecondaryStatus = NtClose(ContDirKeyHandle);
|
|
ASSERT(NT_SUCCESS(SecondaryStatus));
|
|
}
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupIsolatedNames(
|
|
IN ULONG Count,
|
|
IN ULONG IsolatedNameCount,
|
|
IN PLSAPR_UNICODE_STRING Names,
|
|
IN PLSAPR_UNICODE_STRING PrefixNames,
|
|
IN PLSAPR_UNICODE_STRING SuffixNames,
|
|
IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
|
|
IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
|
|
IN PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount,
|
|
IN OUT PULONG MappedIsolatedNameCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to identify isolated names as the names of well known
|
|
Sids or Domains present on the Lookup Path.
|
|
|
|
Names may be either isolated (e.g. JohnH) or composite names containing
|
|
both the domain name and account name. Composite names must include a
|
|
backslash character separating the domain name from the account name
|
|
(e.g. Acctg\JohnH). An isolated name may be either an account name
|
|
(user, group, or alias) or a domain name.
|
|
|
|
Translation of isolated names introduces the possibility of name
|
|
collisions (since the same name may be used in multiple domains). An
|
|
isolated name will be translated using the following algorithm:
|
|
|
|
If the name is a well-known name (e.g. Local or Interactive), then the
|
|
corresponding well-known Sid is returned.
|
|
|
|
If the name is the Built-in Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Account Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Primary Domain's name, then that domain's Sid will
|
|
be returned.
|
|
|
|
If the name is the name of one of the Primary Domain's Trusted Domains,
|
|
then that domain's Sid will be returned.
|
|
|
|
If the name is a user, group, or alias in the Built-in Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
If the name is a user, group, or alias in the Primary Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
Otherwise, the name is not translated.
|
|
|
|
NOTE: Proxy, Machine, and Trust user accounts are not referenced
|
|
for name translation. Only normal user accounts are used for ID
|
|
translation. If translation of other account types is needed, then
|
|
SAM services should be used directly.
|
|
|
|
Arguments:
|
|
|
|
Count - Specifies the number of names to be translated.
|
|
|
|
IsolatedNameCount - Specifies the number of Isolated Names present
|
|
in the list of Names. An Isolated Name is a Name that contains
|
|
only one component, e.g. "ScottBi". Names such as "NtDev\ScottBi"
|
|
containing two components are called Composite Names.
|
|
|
|
Names - Pointer to an array of Count Unicode String structures
|
|
specifying the names to be looked up and mapped to Sids.
|
|
The strings may be names of User, Group or Alias accounts or
|
|
domains.
|
|
|
|
PrefixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Prefix portions of the Names. Names having no
|
|
Prefix are called Isolated Names. For these, the Unicode String
|
|
structure is set to contain a zero Length.
|
|
|
|
SuffixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Suffix portions of the Names.
|
|
|
|
ReferencedDomains - Pointer to a structure in which the list of domains
|
|
used in the translation is maintained. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for each
|
|
translated name, this structure will only contain one component for
|
|
each domain utilized in the translation.
|
|
|
|
TranslatedSids - Pointer to a structure in which the translations to Sids
|
|
corresponding to the Names specified on Names is maintained. The
|
|
nth entry in this array provides a translation (where known) for the
|
|
nth element in the Names parameter.
|
|
|
|
MappedCount - Pointer to location in which a count of the Names that
|
|
have been completely translated is maintained.
|
|
|
|
CompletelyUnmappedCount - Pointer to a location in which a count of the
|
|
Names that have not been translated (either partially, by identification
|
|
of a Domain Prefix, or completely) is maintained.
|
|
|
|
MappedIsolatedNameCount - Pointer to a location in which a count of the
|
|
Isolated Names that have been mapped is maintained.
|
|
|
|
Return Values:
|
|
|
|
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
|
|
to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// First, lookup any Isolated Well Known Names
|
|
//
|
|
|
|
Status = LsapDbLookupIsolatedWellKnownNames(
|
|
Count,
|
|
IsolatedNameCount,
|
|
Names,
|
|
PrefixNames,
|
|
SuffixNames,
|
|
ReferencedDomains,
|
|
TranslatedSids,
|
|
MappedCount,
|
|
CompletelyUnmappedCount,
|
|
MappedIsolatedNameCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupIsolatedNamesError;
|
|
}
|
|
|
|
//
|
|
// If all of the Isolated Names have now been mapped, finish.
|
|
//
|
|
|
|
if (*MappedIsolatedNameCount == IsolatedNameCount) {
|
|
|
|
goto LookupIsolatedNamesFinish;
|
|
}
|
|
|
|
//
|
|
// Next, attempt to identify Isolated Names as Domain Names
|
|
//
|
|
|
|
Status = LsapDbLookupIsolatedDomainNames(
|
|
Count,
|
|
IsolatedNameCount,
|
|
Names,
|
|
PrefixNames,
|
|
SuffixNames,
|
|
BuiltInDomainTrustInformation,
|
|
AccountDomainTrustInformation,
|
|
PrimaryDomainTrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedSids,
|
|
MappedCount,
|
|
CompletelyUnmappedCount,
|
|
MappedIsolatedNameCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupIsolatedNamesError;
|
|
}
|
|
|
|
LookupIsolatedNamesFinish:
|
|
|
|
return(Status);
|
|
|
|
LookupIsolatedNamesError:
|
|
|
|
goto LookupIsolatedNamesFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupIsolatedWellKnownNames(
|
|
IN ULONG Count,
|
|
IN ULONG IsolatedNameCount,
|
|
IN PLSAPR_UNICODE_STRING Names,
|
|
IN PLSAPR_UNICODE_STRING PrefixNames,
|
|
IN PLSAPR_UNICODE_STRING SuffixNames,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount,
|
|
IN OUT PULONG MappedIsolatedNameCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to identify isolated names as the names of well known
|
|
Sids.
|
|
|
|
Names may be either isolated (e.g. JohnH) or composite names containing
|
|
both the domain name and account name. Composite names must include a
|
|
backslash character separating the domain name from the account name
|
|
(e.g. Acctg\JohnH). An isolated name may be either an account name
|
|
(user, group, or alias) or a domain name.
|
|
|
|
Translation of isolated names introduces the possibility of name
|
|
collisions (since the same name may be used in multiple domains). An
|
|
isolated name will be translated using the following algorithm:
|
|
|
|
If the name is a well-known name (e.g. Local or Interactive), then the
|
|
corresponding well-known Sid is returned.
|
|
|
|
If the name is the Built-in Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Account Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Primary Domain's name, then that domain's Sid will
|
|
be returned.
|
|
|
|
If the name is the name of one of the Primary Domain's Trusted Domains,
|
|
then that domain's Sid will be returned.
|
|
|
|
If the name is a user, group, or alias in the Built-in Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
If the name is a user, group, or alias in the Primary Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
Otherwise, the name is not translated.
|
|
|
|
NOTE: Proxy, Machine, and Trust user accounts are not referenced
|
|
for name translation. Only normal user accounts are used for ID
|
|
translation. If translation of other account types is needed, then
|
|
SAM services should be used directly.
|
|
|
|
Arguments:
|
|
|
|
Count - Specifies the number of names to be translated.
|
|
|
|
IsolatedNameCount - Specifies the number of Isolated Names present
|
|
in the list of Names. An Isolated Name is a Name that contains
|
|
only one component, e.g. "ScottBi". Names such as "NtDev\ScottBi"
|
|
containing two components are called Composite Names.
|
|
|
|
Names - Pointer to an array of Count Unicode String structures
|
|
specifying the names to be looked up and mapped to Sids.
|
|
The strings may be names of User, Group or Alias accounts or
|
|
domains.
|
|
|
|
PrefixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Prefix portions of the Names. Names having no
|
|
Prefix are called Isolated Names. For these, the Unicode String
|
|
structure is set to contain a zero Length.
|
|
|
|
SuffixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Suffix portions of the Names.
|
|
|
|
ReferencedDomains - Pointer to a structure in which the list of domains
|
|
used in the translation is maintained. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for each
|
|
translated name, this structure will only contain one component for
|
|
each domain utilized in the translation.
|
|
|
|
TranslatedSids - Pointer to a structure in which the translations to Sids
|
|
corresponding to the Names specified on Names is maintained. The
|
|
nth entry in this array provides a translation (where known) for the
|
|
nth element in the Names parameter.
|
|
|
|
MappedCount - Pointer to location in which a count of the Names that
|
|
have been completely translated is maintained.
|
|
|
|
CompletelyUnmappedCount - Pointer to a location in which a count of the
|
|
Names that have not been translated (either partially, by identification
|
|
of a Domain Prefix, or completely) is maintained.
|
|
|
|
MappedIsolatedNameCount - Pointer to a location in which a count of the
|
|
Isolated Names that have been mapped is maintained.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Names may remain partially or completely unmapped.
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG UpdatedMappedCount;
|
|
ULONG NameNumber;
|
|
ULONG UnmappedNamesRemaining;
|
|
PLSA_TRANSLATED_SID OutputSids;
|
|
LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex;
|
|
LSAPR_TRUST_INFORMATION TrustInformation;
|
|
UCHAR SubAuthorityCount;
|
|
PLSAPR_SID Sid = NULL;
|
|
PLSAPR_SID PrefixSid = NULL;
|
|
ULONG PrefixSidLength;
|
|
ULONG RelativeId;
|
|
OutputSids = TranslatedSids->Sids;
|
|
|
|
//
|
|
// Initialize output parameters.
|
|
//
|
|
|
|
*MappedCount = UpdatedMappedCount = 0;
|
|
UnmappedNamesRemaining = Count - UpdatedMappedCount;
|
|
|
|
//
|
|
// Attempt to identify Names as Well Known Isolated Names
|
|
//
|
|
|
|
for (NameNumber = 0;
|
|
(NameNumber < Count) && (UnmappedNamesRemaining > 0);
|
|
NameNumber++) {
|
|
|
|
//
|
|
// Examine the next entry in the Names array. If the corresponding
|
|
// translated Sid entry has SidTypeUnknown for its Use field, the
|
|
// name has not been translated.
|
|
//
|
|
|
|
if (OutputSids[NameNumber].Use == SidTypeUnknown) {
|
|
|
|
//
|
|
// Attempt to identify the name as the name of a Well Known Sid
|
|
// by using the Well Known Sids table. We skip entries in the
|
|
// table for Sids that are also in the Built In domain. For
|
|
// those, we drop through to the Built in Domain search. Note
|
|
// that only one of these, the Administrators alias is currently
|
|
// present in the table.
|
|
//
|
|
|
|
if (LsapDbLookupIndexWellKnownName(
|
|
&Names[NameNumber],
|
|
&WellKnownSidIndex
|
|
) && (WellKnownSidIndex != LsapAliasAdminsSidIndex)) {
|
|
|
|
//
|
|
// Name is identified. Obtain its Sid. If the
|
|
// SubAuthorityCount for the Sid is positive, extract the
|
|
// Relative Id and place in the translated Sid entry,
|
|
// otherwise store LSA_UNKNOWN_INDEX there.
|
|
//
|
|
|
|
Sid = LsapDbWellKnownSid(WellKnownSidIndex);
|
|
|
|
SubAuthorityCount = *RtlSubAuthorityCountSid((PSID) Sid);
|
|
|
|
RelativeId = LSA_UNKNOWN_ID;
|
|
|
|
PrefixSid = NULL;
|
|
|
|
//
|
|
// Get the Sid's Use.
|
|
//
|
|
|
|
OutputSids[NameNumber].Use =
|
|
LsapDbWellKnownSidNameUse(WellKnownSidIndex);
|
|
|
|
//
|
|
// If the Sid is a Domain Sid, store pointer to
|
|
// it in the Trust Information.
|
|
//
|
|
|
|
if (OutputSids[NameNumber].Use == SidTypeDomain) {
|
|
|
|
TrustInformation.Sid = Sid;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The Sid is not a domain Sid. Construct the Relative Id
|
|
// and Prefix Sid. This is equal to the original Sid
|
|
// excluding the lowest subauthority (Relative id).
|
|
//
|
|
|
|
if (SubAuthorityCount > 0) {
|
|
|
|
RelativeId = *RtlSubAuthoritySid((PSID) Sid, SubAuthorityCount - 1);
|
|
}
|
|
|
|
PrefixSidLength = RtlLengthRequiredSid(
|
|
SubAuthorityCount - 1
|
|
);
|
|
|
|
|
|
PrefixSid = MIDL_user_allocate( PrefixSidLength );
|
|
|
|
if (PrefixSid == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
|
|
RtlCopyMemory( PrefixSid, Sid, PrefixSidLength );
|
|
|
|
(*RtlSubAuthorityCountSid( (PSID) PrefixSid ))--;
|
|
|
|
TrustInformation.Sid = PrefixSid;
|
|
}
|
|
|
|
//
|
|
// Set the Relative Id. For a Domain Sid this is set to the
|
|
// Unknown Value.
|
|
//
|
|
|
|
OutputSids[NameNumber].RelativeId = RelativeId;
|
|
|
|
//
|
|
// Lookup this Domain Sid or Prefix Sid in the Referenced Domain
|
|
// List. If it is already there, return the DomainIndex for the
|
|
// existing entry and free up the memory allocated for the
|
|
// Prefix Sid (if any).
|
|
//
|
|
|
|
if (LsapDbLookupListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation.Sid,
|
|
(PLONG) &OutputSids[NameNumber].DomainIndex
|
|
)) {
|
|
|
|
UnmappedNamesRemaining--;
|
|
|
|
if (PrefixSid != NULL) {
|
|
|
|
MIDL_user_free( PrefixSid );
|
|
PrefixSid = NULL;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// This Domain or Prefix Sid is not currently on the
|
|
// Referenced Domain List. Complete a Trust Information
|
|
// entry and add it to the List. Copy in the Domain Name
|
|
// (Domain Sids) or NULL string. Note that we use
|
|
// RtlCopyMemory to copy a UNICODE_STRING structure onto
|
|
// a LSAPR_UNICODE_STRING structure.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
&TrustInformation.Name,
|
|
&WellKnownSids[WellKnownSidIndex].DomainName,
|
|
sizeof(UNICODE_STRING)
|
|
);
|
|
|
|
//
|
|
// Make an entry in the list of Referenced Domains. Note
|
|
// that in the case of well-known Sids, the Prefix Sid
|
|
// may or may not be the Sid of a Domain. For those well
|
|
// known Sids whose Prefix Sid is not a domain Sid, the
|
|
// Name field in the Trust Information has been set to the
|
|
// NULL string.
|
|
//
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
ReferencedDomains,
|
|
&TrustInformation,
|
|
(PLONG) &OutputSids[NameNumber].DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we allocated memory for a Prefix Sid, free it.
|
|
//
|
|
|
|
if (PrefixSid != NULL) {
|
|
|
|
MIDL_user_free( PrefixSid );
|
|
PrefixSid = NULL;
|
|
}
|
|
|
|
UnmappedNamesRemaining--;
|
|
(*MappedIsolatedNameCount)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupIsolatedWellKnownNamesError;
|
|
}
|
|
|
|
//
|
|
// Set the output parameters in the success case..
|
|
//
|
|
|
|
TranslatedSids->Sids = OutputSids;
|
|
TranslatedSids->Entries = Count;
|
|
*MappedCount = Count - UnmappedNamesRemaining;
|
|
*CompletelyUnmappedCount = UnmappedNamesRemaining;
|
|
|
|
LookupIsolatedWellKnownNamesFinish:
|
|
|
|
//
|
|
// If we still have memory allocated for the a Prefix Sid, free it.
|
|
//
|
|
|
|
if (PrefixSid != NULL) {
|
|
|
|
MIDL_user_free( PrefixSid );
|
|
PrefixSid = NULL;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupIsolatedWellKnownNamesError:
|
|
|
|
goto LookupIsolatedWellKnownNamesFinish;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
LsapDbLookupIndexWellKnownName(
|
|
IN OPTIONAL PLSAPR_UNICODE_STRING Name,
|
|
OUT PLSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function looks up a Name to determine if it is well-known. If so,
|
|
an index into the table of well-known Sids is returned.
|
|
|
|
Arguments:
|
|
|
|
Name - Pointer to Name to be looked up. If a NULL pointer or
|
|
pointer to a zero length string is specified, FALSE will
|
|
always be returned.
|
|
|
|
WellKnownSidIndex - Pointer to variable that will receive the
|
|
index of the Name if well known.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the Name is well-known, else FALSE
|
|
|
|
--*/
|
|
|
|
{
|
|
LSAP_WELL_KNOWN_SID_INDEX Index;
|
|
PLSAPR_UNICODE_STRING MatchName = NULL;
|
|
BOOLEAN BooleanStatus = FALSE;
|
|
|
|
if ((!ARGUMENT_PRESENT(Name)) || Name->Length == 0 ) {
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Scan the table of well-known Sids looking for a match on Name.
|
|
//
|
|
|
|
for(Index = LsapWorldSidIndex; Index<LsapDummyLastSidIndex; Index++) {
|
|
|
|
//
|
|
// Allow NULL entries in the table of well-known Sids for now.
|
|
//
|
|
|
|
if (WellKnownSids[Index].Sid == NULL) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If the current entry in the table of Well Known Sids
|
|
// is for a Domain Sid, match the name with the DomainName
|
|
// field. Otherwise, match it with the Name field.
|
|
//
|
|
|
|
if (WellKnownSids[Index].Use == SidTypeDomain) {
|
|
|
|
MatchName = (PLSAPR_UNICODE_STRING) &WellKnownSids[Index].DomainName;
|
|
|
|
if (RtlEqualDomainName(
|
|
(PUNICODE_STRING) Name,
|
|
(PUNICODE_STRING) MatchName
|
|
)) {
|
|
|
|
//
|
|
// If a match is found, return the index to the caller.
|
|
//
|
|
|
|
BooleanStatus = TRUE;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
MatchName = (PLSAPR_UNICODE_STRING) &WellKnownSids[Index].Name;
|
|
|
|
if (RtlEqualUnicodeString(
|
|
(PUNICODE_STRING) Name,
|
|
(PUNICODE_STRING) MatchName,
|
|
TRUE
|
|
)) {
|
|
|
|
//
|
|
// If a match is found, return the index to the caller.
|
|
//
|
|
|
|
BooleanStatus = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*WellKnownSidIndex = Index;
|
|
|
|
return(BooleanStatus);
|
|
}
|
|
|
|
|
|
PUNICODE_STRING
|
|
LsapDbWellKnownSidName(
|
|
IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the Unicode Name of a Well Known Sid.
|
|
|
|
Arguments:
|
|
|
|
WellKnownSidIndex - Index into the Well Known Sid information table.
|
|
It is the caller's responsibility to ensure that the given index
|
|
is valid.
|
|
|
|
Return Value:
|
|
|
|
PUNICODE_STRING Pointer to the name of the Well Known Sid.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// If the Sid is a Domain Sid, its name is contained within the
|
|
// DomainName field in the Well Known Sids table. If the Sid is not a
|
|
// Domain Sid, its name is contained within the Name field of the entry.
|
|
//
|
|
|
|
if (WellKnownSids[WellKnownSidIndex].Use == SidTypeDomain) {
|
|
|
|
return(&WellKnownSids[WellKnownSidIndex].DomainName);
|
|
|
|
} else {
|
|
|
|
return(&WellKnownSids[WellKnownSidIndex].Name);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupIsolatedDomainNames(
|
|
IN ULONG Count,
|
|
IN ULONG IsolatedNameCount,
|
|
IN PLSAPR_UNICODE_STRING Names,
|
|
IN PLSAPR_UNICODE_STRING PrefixNames,
|
|
IN PLSAPR_UNICODE_STRING SuffixNames,
|
|
IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
|
|
IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
|
|
IN PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount,
|
|
IN OUT PULONG MappedIsolatedNameCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to identify isolated names as the names of Domains.
|
|
|
|
Names may be either isolated (e.g. JohnH) or composite names containing
|
|
both the domain name and account name. Composite names must include a
|
|
backslash character separating the domain name from the account name
|
|
(e.g. Acctg\JohnH). An isolated name may be either an account name
|
|
(user, group, or alias) or a domain name.
|
|
|
|
Translation of isolated names introduces the possibility of name
|
|
collisions (since the same name may be used in multiple domains). An
|
|
isolated name will be translated using the following algorithm:
|
|
|
|
If the name is a well-known name (e.g. Local or Interactive), then the
|
|
corresponding well-known Sid is returned.
|
|
|
|
If the name is the Built-in Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Account Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Primary Domain's name, then that domain's Sid will
|
|
be returned.
|
|
|
|
If the name is the name of one of the Primary Domain's Trusted Domains,
|
|
then that domain's Sid will be returned.
|
|
|
|
If the name is a user, group, or alias in the Built-in Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
If the name is a user, group, or alias in the Primary Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
Otherwise, the name is not translated.
|
|
|
|
NOTE: Proxy, Machine, and Trust user accounts are not referenced
|
|
for name translation. Only normal user accounts are used for ID
|
|
translation. If translation of other account types is needed, then
|
|
SAM services should be used directly.
|
|
|
|
Arguments:
|
|
|
|
Count - Specifies the number of names to be translated.
|
|
|
|
IsolatedNameCount - Specifies the number of Isolated Names present
|
|
in the list of Names. An Isolated Name is a Name that contains
|
|
only one component, e.g. "ScottBi". Names such as "NtDev\ScottBi"
|
|
containing two components are called Composite Names.
|
|
|
|
Names - Pointer to an array of Count Unicode String structures
|
|
specifying the names to be looked up and mapped to Sids.
|
|
The strings may be names of User, Group or Alias accounts or
|
|
domains.
|
|
|
|
PrefixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Prefix portions of the Names. Names having no
|
|
Prefix are called Isolated Names. For these, the Unicode String
|
|
structure is set to contain a zero Length.
|
|
|
|
SuffixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Suffix portions of the Names.
|
|
|
|
ReferencedDomains - Pointer to a structure in which the list of domains
|
|
used in the translation is maintained. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for each
|
|
translated name, this structure will only contain one component for
|
|
each domain utilized in the translation.
|
|
|
|
TranslatedSids - Pointer to a structure in which the translations to Sids
|
|
corresponding to the Names specified on Names is maintained. The
|
|
nth entry in this array provides a translation (where known) for the
|
|
nth element in the Names parameter.
|
|
|
|
MappedCount - Pointer to location in which a count of the Names that
|
|
have been completely translated is maintained.
|
|
|
|
CompletelyUnmappedCount - Pointer to a location in which a count of the
|
|
Names that have not been translated (either partially, by identification
|
|
of a Domain Prefix, or completely) is maintained.
|
|
|
|
MappedIsolatedNameCount - Pointer to a location in which a count of the
|
|
Isolated Names that have been mapped is maintained.
|
|
|
|
Return Values:
|
|
|
|
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
|
|
to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status = STATUS_SUCCESS,
|
|
IgnoreStatus;
|
|
|
|
NTSTATUS SecondaryStatus = STATUS_SUCCESS;
|
|
|
|
ULONG
|
|
NameIndex;
|
|
|
|
PLSAPR_POLICY_ACCOUNT_DOM_INFO
|
|
PolicyAccountDomainInfo = NULL;
|
|
|
|
PLSAPR_TRUST_INFORMATION
|
|
TrustInformation = NULL;
|
|
|
|
LSAP_DB_TRUSTED_DOMAIN_LIST
|
|
PDTrustedDomainList;
|
|
|
|
PLSAP_DB_TRUSTED_DOMAIN_LIST
|
|
TrustedDomainList = NULL;
|
|
|
|
PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION
|
|
TrustedDomainListSection = NULL;
|
|
|
|
BOOLEAN
|
|
PrimaryDomainAccessible = TRUE;
|
|
|
|
LSA_HANDLE
|
|
ControllerPolicyHandle = NULL;
|
|
|
|
//
|
|
// Search for Isolated Names that match the Built-In Domain Name,
|
|
// Account Domain Name or one of the Primary Domain's Trusted Domain
|
|
// Names.
|
|
//
|
|
|
|
PDTrustedDomainList.Valid = FALSE;
|
|
|
|
for (NameIndex = 0;
|
|
(NameIndex < Count) && (*MappedIsolatedNameCount < IsolatedNameCount);
|
|
NameIndex++) {
|
|
|
|
//
|
|
// Skip this name if already mapped or partially mapped.
|
|
//
|
|
|
|
if (!LsapDbCompletelyUnmappedSid(&TranslatedSids->Sids[NameIndex])) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Skip this name if composite.
|
|
//
|
|
|
|
if (PrefixNames[ NameIndex ].Length != (USHORT) 0) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We've found an Isolated Name. First check if it is the
|
|
// name of the Built In Domain.
|
|
//
|
|
|
|
Status = LsapDbLookupIsolatedDomainName(
|
|
NameIndex,
|
|
&SuffixNames[NameIndex],
|
|
BuiltInDomainTrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedSids,
|
|
MappedCount,
|
|
CompletelyUnmappedCount,
|
|
MappedIsolatedNameCount
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if (Status != STATUS_NONE_MAPPED) {
|
|
|
|
break;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Isolated Name is not the name of the Built-in Domain. See if
|
|
// it is the name of the Accounts Domain.
|
|
//
|
|
|
|
Status = LsapDbLookupIsolatedDomainName(
|
|
NameIndex,
|
|
&SuffixNames[NameIndex],
|
|
AccountDomainTrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedSids,
|
|
MappedCount,
|
|
CompletelyUnmappedCount,
|
|
MappedIsolatedNameCount
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// If we are configured as a member of a Work Group, there is
|
|
// no Primary or Trusted Domain List to search, so skip to next
|
|
// name. We are configured as a member of a Work Group if and
|
|
// only if out PolicyPrimaryDomainInformation contains a NULL Sid.
|
|
//
|
|
|
|
if (PrimaryDomainTrustInformation->Sid == NULL) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Isolated Name is not the name of either the Built-in or Accounts
|
|
// Domain. Try the Primary Domain if this is different from the
|
|
// Accounts Domain.
|
|
//
|
|
|
|
if (!RtlEqualDomainName(
|
|
(PUNICODE_STRING) &PrimaryDomainTrustInformation->Name,
|
|
(PUNICODE_STRING) &AccountDomainTrustInformation->Name
|
|
)) {
|
|
|
|
|
|
Status = LsapDbLookupIsolatedDomainName(
|
|
NameIndex,
|
|
&SuffixNames[NameIndex],
|
|
PrimaryDomainTrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedSids,
|
|
MappedCount,
|
|
CompletelyUnmappedCount,
|
|
MappedIsolatedNameCount
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if (Status != STATUS_NONE_MAPPED) {
|
|
|
|
break;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// See if the Isolated Name matches one of the Primary Domain's
|
|
// Trusted Domains or, if we are a DC, one of our Trusted Domains.
|
|
// If we are a Workstation, we first need to enumerate the Trusted Domains
|
|
// on the Primary Domain. If the Name is a Trusted Domain,
|
|
// translate it.
|
|
//
|
|
|
|
if (LsapProductType != NtProductLanManNt) {
|
|
|
|
if ((!PDTrustedDomainList.Valid) && PrimaryDomainAccessible) {
|
|
|
|
//
|
|
// We are a Workstation. Use the Trusted Domain List
|
|
// for the Primary Domain after first constructing a copy
|
|
// of it.
|
|
//
|
|
|
|
TrustedDomainList = &PDTrustedDomainList;
|
|
|
|
//
|
|
// Open the Policy object on some DC for the Primary Domain.
|
|
//
|
|
|
|
Status = LsapDbOpenPolicyTrustedDomain(
|
|
PrimaryDomainTrustInformation,
|
|
POLICY_VIEW_LOCAL_INFORMATION,
|
|
&ControllerPolicyHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// We are unable to connect to the Primary Domain.
|
|
// This simply means that we limit our search for Isolated
|
|
// Names to the local domains, so mask the error and continue.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
PrimaryDomainAccessible = FALSE;
|
|
TrustedDomainList = NULL;
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// We've successfully opened a DC for the Primary Domain.
|
|
// Now build a list of its Trusted Domains.
|
|
//
|
|
|
|
Status = LsapDbBuildTrustedDomainList(
|
|
ControllerPolicyHandle,
|
|
TrustedDomainList
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
PDTrustedDomainList.Valid = FALSE;
|
|
PrimaryDomainAccessible = FALSE;
|
|
SecondaryStatus = LsaClose(ControllerPolicyHandle);
|
|
ControllerPolicyHandle = NULL;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
ASSERT (LsapProductType == NtProductLanManNt);
|
|
|
|
//
|
|
// We are a DC. Use the Local Trusted Domain List.
|
|
//
|
|
|
|
TrustedDomainList = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// Lookup the Isolated Name in the appropriate Trusted Domain List.
|
|
//
|
|
|
|
Status = LsapDbLookupNameTrustedDomainList(
|
|
TrustedDomainList,
|
|
&SuffixNames[NameIndex],
|
|
&TrustInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (Status != STATUS_NO_SUCH_DOMAIN) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This Isolated Name is not the Name of a Trusted Domain.
|
|
// Skip to the next Name.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if ( ControllerPolicyHandle != NULL) {
|
|
|
|
SecondaryStatus = LsaClose( ControllerPolicyHandle );
|
|
ControllerPolicyHandle = NULL;
|
|
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// This Isolated Name is a Trusted Domain. Translate it.
|
|
//
|
|
|
|
Status = LsapDbLookupIsolatedDomainName(
|
|
NameIndex,
|
|
&SuffixNames[NameIndex],
|
|
TrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedSids,
|
|
MappedCount,
|
|
CompletelyUnmappedCount,
|
|
MappedIsolatedNameCount
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if ( ControllerPolicyHandle != NULL) {
|
|
|
|
SecondaryStatus = LsaClose( ControllerPolicyHandle );
|
|
ControllerPolicyHandle = NULL;
|
|
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (Status != STATUS_NONE_MAPPED) {
|
|
|
|
break;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupIsolatedDomainNamesError;
|
|
}
|
|
|
|
LookupIsolatedDomainNamesFinish:
|
|
|
|
//
|
|
// Clean up any trusted domain list we might have created.
|
|
//
|
|
|
|
if (PDTrustedDomainList.Valid) {
|
|
IgnoreStatus = LsapDbDestroyTrustedDomainList( &PDTrustedDomainList );
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
}
|
|
|
|
if ( ControllerPolicyHandle != NULL) {
|
|
|
|
SecondaryStatus = LsaClose( ControllerPolicyHandle );
|
|
ControllerPolicyHandle = NULL;
|
|
|
|
if (!NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = SecondaryStatus;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return(Status);
|
|
|
|
LookupIsolatedDomainNamesError:
|
|
|
|
goto LookupIsolatedDomainNamesFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupAddListReferencedDomains(
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
OUT PLONG DomainIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function searches a Referenced Domain List for an entry for a
|
|
given domain and, if no entry exists, adds new entry. If an entry
|
|
id added, its index into the Referenced Domain List is returned,
|
|
otherwise the index of the existing entry is returned. If an entry
|
|
needs to be added and there is insufficient room in the list provided
|
|
for the new entry, the list will be created or grown as necessary.
|
|
|
|
Arguments:
|
|
|
|
ReferencedDomains - Pointer to a structure in which the list of domains
|
|
used in the translation is maintained. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for each
|
|
translated name, this structure will only contain one component for
|
|
each domain utilized in the translation.
|
|
|
|
TrustInformation - Points to Trust Information for the domain being
|
|
added to the list. On exit, the DomainIndex parameter will be set to the
|
|
index of the entry on the Referenced Domain List; a negative
|
|
value will be stored in the error case.
|
|
|
|
DomainIndex - Pointer to location that receives the index of the
|
|
newly added or existing entry for the domain within the
|
|
Referenced Domain List. In the error case, a negative value
|
|
is returned.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG NextIndex;
|
|
LSAPR_TRUST_INFORMATION OutputTrustInformation;
|
|
|
|
OutputTrustInformation.Name.Buffer = NULL;
|
|
OutputTrustInformation.Sid = NULL;
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
if (ReferencedDomains == NULL) {
|
|
|
|
goto AddListReferencedDomainsError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Search the existing list, trying to match the Domain Sid in the
|
|
// provided Trust Information with the Domain Sid in a Referenced Domain
|
|
// List entry. If an entry is found with matching Sid, just return
|
|
// that entry's index.
|
|
//
|
|
|
|
if (LsapDbLookupListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation->Sid,
|
|
DomainIndex
|
|
)) {
|
|
|
|
goto AddListReferencedDomainsFinish;
|
|
}
|
|
|
|
//
|
|
// Check that there is enough room in the List provided for one more
|
|
// entry. If not, grow the list, copying and freeing the old.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (ReferencedDomains->Entries >= ReferencedDomains->MaxEntries) {
|
|
|
|
Status = LsapDbLookupGrowListReferencedDomains(
|
|
ReferencedDomains,
|
|
ReferencedDomains->MaxEntries +
|
|
LSAP_DB_REF_DOMAIN_DELTA
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto AddListReferencedDomainsError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We now have a Referenced Domain List with room for at least one more
|
|
// entry. Copy in the Trust Information.
|
|
//
|
|
|
|
NextIndex = ReferencedDomains->Entries;
|
|
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
NULL,
|
|
(PUNICODE_STRING) &OutputTrustInformation.Name,
|
|
(PUNICODE_STRING) &TrustInformation->Name
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto AddListReferencedDomainsError;
|
|
}
|
|
|
|
Status = LsapRpcCopySid(
|
|
NULL,
|
|
(PSID) &OutputTrustInformation.Sid,
|
|
(PSID) TrustInformation->Sid
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto AddListReferencedDomainsError;
|
|
}
|
|
|
|
ReferencedDomains->Domains[NextIndex] = OutputTrustInformation;
|
|
*DomainIndex = (LONG) NextIndex;
|
|
ReferencedDomains->Entries++;
|
|
|
|
AddListReferencedDomainsFinish:
|
|
|
|
return(Status);
|
|
|
|
AddListReferencedDomainsError:
|
|
|
|
//
|
|
// Cleanup buffers allocated for Output Trust Information structure.
|
|
//
|
|
|
|
if (OutputTrustInformation.Name.Buffer != NULL) {
|
|
|
|
MIDL_user_free( OutputTrustInformation.Name.Buffer );
|
|
}
|
|
|
|
if (OutputTrustInformation.Sid != NULL) {
|
|
|
|
MIDL_user_free( OutputTrustInformation.Sid );
|
|
}
|
|
|
|
goto AddListReferencedDomainsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupCreateListReferencedDomains(
|
|
OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
|
|
IN ULONG InitialMaxEntries
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates an empty Referenced Domain List. The caller
|
|
is responsible for cleaning up this list when no longer required.
|
|
|
|
Arguments:
|
|
|
|
ReferencedDomains - Receives a pointer to the newly created empty
|
|
Referenced Domain List.
|
|
|
|
InitialMaxEntries - Initial maximum number of entries desired.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient System Resources
|
|
such as memory, to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG DomainsLength;
|
|
PLSAPR_TRUST_INFORMATION Domains = NULL;
|
|
PVOID Buffers[2];
|
|
ULONG BufferCount;
|
|
ULONG Index;
|
|
PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
|
|
|
|
//
|
|
// Allocate the Referenced Domain List header.
|
|
//
|
|
|
|
BufferCount = 0;
|
|
|
|
OutputReferencedDomains = MIDL_user_allocate(
|
|
sizeof(LSAP_DB_REFERENCED_DOMAIN_LIST)
|
|
);
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (OutputReferencedDomains == NULL) {
|
|
|
|
goto CreateListReferencedDomainsError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Buffers[BufferCount] = OutputReferencedDomains;
|
|
BufferCount++;
|
|
|
|
//
|
|
// If a non-zero initial entry count, allocate an array of Trust Information
|
|
// entries.
|
|
//
|
|
|
|
if (InitialMaxEntries > 0) {
|
|
|
|
DomainsLength = sizeof(LSA_TRUST_INFORMATION) * InitialMaxEntries;
|
|
Domains = MIDL_user_allocate( DomainsLength );
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (Domains == NULL) {
|
|
|
|
goto CreateListReferencedDomainsError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Buffers[BufferCount] = Domains;
|
|
BufferCount++;
|
|
|
|
RtlZeroMemory( Domains, DomainsLength );
|
|
}
|
|
|
|
//
|
|
// Initialize the Referenced Domain List Header
|
|
//
|
|
|
|
OutputReferencedDomains->Entries = 0;
|
|
OutputReferencedDomains->MaxEntries = InitialMaxEntries;
|
|
OutputReferencedDomains->Domains = Domains;
|
|
|
|
CreateListReferencedDomainsFinish:
|
|
|
|
*ReferencedDomains = OutputReferencedDomains;
|
|
return(Status);
|
|
|
|
CreateListReferencedDomainsError:
|
|
|
|
//
|
|
// Free up buffers allocated bu this routine.
|
|
//
|
|
|
|
for (Index = 0; Index < BufferCount; Index++) {
|
|
|
|
MIDL_user_free(Buffers[Index]);
|
|
}
|
|
|
|
goto CreateListReferencedDomainsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupGrowListReferencedDomains(
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN ULONG MaxEntries
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function expands a Referenced Domain List to contain the
|
|
specified maximum number of entries. The memory for the old Domains
|
|
array is released.
|
|
|
|
Arguments:
|
|
|
|
ReferencedDomains - Pointer to a Referenced Domain List. This
|
|
list references an array of zero or more Trust Information
|
|
entries describing each of the domains referenced by the names.
|
|
This array will be appended to/reallocated if necessary.
|
|
|
|
MaxEntries - New maximum number of entries.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources to
|
|
complete the call.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAPR_TRUST_INFORMATION NewDomainsInfo = NULL;
|
|
PLSAPR_TRUST_INFORMATION OldDomainsInfo = NULL;
|
|
ULONG OldDomainsInfoLength, NewDomainsInfoLength;
|
|
|
|
if (ReferencedDomains->MaxEntries < MaxEntries) {
|
|
|
|
NewDomainsInfoLength = MaxEntries * sizeof (LSA_TRUST_INFORMATION);
|
|
OldDomainsInfoLength =
|
|
ReferencedDomains->MaxEntries * sizeof (LSA_TRUST_INFORMATION);
|
|
|
|
NewDomainsInfo = MIDL_user_allocate( NewDomainsInfoLength );
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (NewDomainsInfo == NULL) {
|
|
|
|
goto GrowListReferencedDomainsError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// If there was an existing Trust Information Array, copy it
|
|
// to the newly allocated one and free it.
|
|
//
|
|
|
|
OldDomainsInfo = ReferencedDomains->Domains;
|
|
|
|
if (OldDomainsInfo != NULL) {
|
|
|
|
RtlCopyMemory( NewDomainsInfo, OldDomainsInfo, OldDomainsInfoLength );
|
|
MIDL_user_free( OldDomainsInfo );
|
|
}
|
|
|
|
ReferencedDomains->Domains = NewDomainsInfo;
|
|
ReferencedDomains->MaxEntries = MaxEntries;
|
|
}
|
|
|
|
GrowListReferencedDomainsFinish:
|
|
|
|
return(Status);
|
|
|
|
GrowListReferencedDomainsError:
|
|
|
|
goto GrowListReferencedDomainsFinish;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
LsapDbLookupListReferencedDomains(
|
|
IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN PLSAPR_SID DomainSid,
|
|
OUT PLONG DomainIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function searches a Referenced Domain List for a given domain
|
|
and, if found, returns the index of the domain's entry in the list.
|
|
If the domain is not found an error is returned and a negative value
|
|
is returned.
|
|
|
|
Arguments:
|
|
|
|
ReferencedDomains - Pointer to a Referenced Domain List. This
|
|
list references an array of zero or more Trust Information
|
|
entries describing each of the domains referenced by the names.
|
|
This array will be appended to/reallocated if necessary.
|
|
|
|
DomainSid - Information containing the Domain's Sid.
|
|
|
|
DomainIndex - Pointer to location that receives the index of the domain
|
|
in the Referenced Domain List if the domin is found, otherwise
|
|
a negative value.
|
|
|
|
Return Values:
|
|
|
|
BOOLEAN - TRUE if entry found, else FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN BooleanStatus = FALSE;
|
|
LONG Index;
|
|
LONG Entries;
|
|
PLSAPR_TRUST_INFORMATION DomainsInfo;
|
|
|
|
//
|
|
// Search the Referenced Domain List by Sid or by Name
|
|
//
|
|
|
|
Entries = (LONG) ReferencedDomains->Entries;
|
|
DomainsInfo = ReferencedDomains->Domains;
|
|
*DomainIndex = LSA_UNKNOWN_INDEX;
|
|
|
|
for (Index = 0; Index < (LONG) Entries; Index++) {
|
|
|
|
if (RtlEqualSid( (PSID) DomainsInfo[Index].Sid, (PSID) DomainSid)) {
|
|
|
|
BooleanStatus = TRUE;
|
|
*DomainIndex = Index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(BooleanStatus);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupMergeDisjointReferencedDomains(
|
|
IN OPTIONAL PLSAPR_REFERENCED_DOMAIN_LIST FirstReferencedDomainList,
|
|
IN OPTIONAL PLSAPR_REFERENCED_DOMAIN_LIST SecondReferencedDomainList,
|
|
OUT PLSAPR_REFERENCED_DOMAIN_LIST *OutputReferencedDomainList,
|
|
IN ULONG Options
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function merges disjoint Referenced Domain Lists, producing a third
|
|
list. The output list is always produced in non allocate(all_nodes) form.
|
|
|
|
Arguments:
|
|
|
|
FirstReferencedDomainList - Pointer to first mergand.
|
|
|
|
SecondReferencedDomainList - Pointer to second mergand.
|
|
|
|
OutputReferencedDomainList - Receives a pointer to the output list.
|
|
|
|
Options - Specifies optional actions
|
|
|
|
LSAP_DB_USE_FIRST_MERGAND_GRAPH - Specifies that the resulting
|
|
merged Referenced Domain List may reference the graph of
|
|
pointers in the first Referenced Domain list. This option
|
|
is normally selected, since that graph has been allocated
|
|
as individual nodes.
|
|
|
|
LSAP_DB_USE_SECOND_MERGAND_GRAPH - Specifies that the resulting
|
|
merged Referenced Domain List may reference the graph of
|
|
pointers in the first Referenced Domain list. This option
|
|
is normally not selected, since that graph is usually allocated
|
|
as all_nodes.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG TotalEntries;
|
|
ULONG FirstReferencedDomainListLength;
|
|
ULONG SecondReferencedDomainListLength;
|
|
ULONG FirstEntries, SecondEntries;
|
|
LSAP_MM_FREE_LIST FreeList;
|
|
ULONG NextEntry;
|
|
ULONG MaximumFreeListEntries;
|
|
ULONG CleanupFreeListOptions = (ULONG) 0;
|
|
|
|
//
|
|
// Calculate Size of output Referenced Domain List.
|
|
//
|
|
|
|
FirstEntries = (ULONG) 0;
|
|
|
|
if (FirstReferencedDomainList != NULL) {
|
|
|
|
FirstEntries = FirstReferencedDomainList->Entries;
|
|
}
|
|
|
|
SecondEntries = (ULONG) 0;
|
|
|
|
if (SecondReferencedDomainList != NULL) {
|
|
|
|
SecondEntries = SecondReferencedDomainList->Entries;
|
|
}
|
|
|
|
TotalEntries = FirstEntries + SecondEntries;
|
|
|
|
//
|
|
// Allocate a Free List for error cleanup. We need two entries
|
|
// per Referenced Domain List entry, one for the Domain Name buffer
|
|
// and one for the Domain Sid.
|
|
//
|
|
|
|
MaximumFreeListEntries = (ULONG) 0;
|
|
|
|
if (!(Options & LSAP_DB_USE_FIRST_MERGAND_GRAPH)) {
|
|
|
|
MaximumFreeListEntries += 2*FirstEntries;
|
|
}
|
|
|
|
if (!(Options & LSAP_DB_USE_SECOND_MERGAND_GRAPH)) {
|
|
|
|
MaximumFreeListEntries += 2*SecondEntries;
|
|
}
|
|
|
|
Status = LsapMmCreateFreeList( &FreeList, MaximumFreeListEntries );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto MergeDisjointDomainsError;
|
|
}
|
|
|
|
Status = LsapDbLookupCreateListReferencedDomains(
|
|
OutputReferencedDomainList,
|
|
TotalEntries
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto MergeDisjointDomainsError;
|
|
}
|
|
|
|
//
|
|
// Set the number of entries used. We will use all of the entries,
|
|
// so set this value to the Maximum number of Entries.
|
|
//
|
|
|
|
(*OutputReferencedDomainList)->Entries = TotalEntries;
|
|
|
|
//
|
|
// Copy in the entries (if any) from the first list.
|
|
//
|
|
|
|
FirstReferencedDomainListLength =
|
|
FirstEntries * sizeof(LSA_TRUST_INFORMATION);
|
|
|
|
if (FirstReferencedDomainListLength > (ULONG) 0) {
|
|
|
|
if (Options & LSAP_DB_USE_FIRST_MERGAND_GRAPH) {
|
|
|
|
RtlCopyMemory(
|
|
(*OutputReferencedDomainList)->Domains,
|
|
FirstReferencedDomainList->Domains,
|
|
FirstReferencedDomainListLength
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// The graph of the first Referenced Domain List must be
|
|
// copied to separately allocated memory buffers.
|
|
// Copy each of the Trust Information entries, allocating
|
|
// individual memory buffers for each Domain Name and Sid.
|
|
//
|
|
|
|
for (NextEntry = 0; NextEntry < FirstEntries; NextEntry++) {
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
&FreeList,
|
|
(PUNICODE_STRING) &((*OutputReferencedDomainList)->Domains[NextEntry].Name),
|
|
(PUNICODE_STRING) &FirstReferencedDomainList->Domains[NextEntry].Name
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
Status = LsapRpcCopySid(
|
|
&FreeList,
|
|
(PSID) &((*OutputReferencedDomainList)->Domains[NextEntry].Sid),
|
|
(PSID) FirstReferencedDomainList->Domains[NextEntry].Sid
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto MergeDisjointDomainsError;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy in the entries (if any) from the second list.
|
|
//
|
|
|
|
SecondReferencedDomainListLength =
|
|
SecondEntries * sizeof(LSA_TRUST_INFORMATION);
|
|
|
|
if (SecondReferencedDomainListLength > (ULONG) 0) {
|
|
|
|
if (Options & LSAP_DB_USE_SECOND_MERGAND_GRAPH) {
|
|
|
|
RtlCopyMemory(
|
|
(*OutputReferencedDomainList)->Domains + FirstReferencedDomainList->Entries,
|
|
SecondReferencedDomainList->Domains,
|
|
SecondReferencedDomainListLength
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Copy each of the Trust Information entries, allocating
|
|
// individual memory buffers for each Domain Name and Sid.
|
|
//
|
|
|
|
for (NextEntry = 0; NextEntry < SecondEntries; NextEntry++) {
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
&FreeList,
|
|
(PUNICODE_STRING) &((*OutputReferencedDomainList)->Domains[FirstEntries +NextEntry].Name),
|
|
(PUNICODE_STRING) &SecondReferencedDomainList->Domains[NextEntry].Name
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
Status = LsapRpcCopySid(
|
|
&FreeList,
|
|
(PSID) &((*OutputReferencedDomainList)->Domains[FirstEntries +NextEntry].Sid),
|
|
(PSID) SecondReferencedDomainList->Domains[NextEntry].Sid
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto MergeDisjointDomainsError;
|
|
}
|
|
}
|
|
}
|
|
|
|
MergeDisjointDomainsFinish:
|
|
|
|
//
|
|
// Delete the Free List, freeing buffers on the list if an error
|
|
// occurred.
|
|
//
|
|
|
|
LsapMmCleanupFreeList( &FreeList, CleanupFreeListOptions );
|
|
return(Status);
|
|
|
|
MergeDisjointDomainsError:
|
|
|
|
//
|
|
// Specify that buffers on Free List are to be freed.
|
|
//
|
|
|
|
CleanupFreeListOptions |= LSAP_MM_FREE_BUFFERS;
|
|
goto MergeDisjointDomainsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupSidsInLocalDomains(
|
|
IN ULONG Count,
|
|
IN PLSAPR_SID *Sids,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount,
|
|
IN ULONG Options
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function looks up Sids in the local SAM domains and attempts to
|
|
translate them to names. Currently, there are two local SAM domains,
|
|
the Built-in domain (which has a well-known Sid and name) and the
|
|
Account Domain (which has a configurable Sid and name).
|
|
|
|
Arguments:
|
|
|
|
Count - Number of Sids in the Sids array, Note that some of these
|
|
may already have been mapped elsewhere, as specified by the
|
|
MappedCount parameter.
|
|
|
|
Sids - Pointer to array of pointers to Sids to be translated.
|
|
Zero or all of the Sids may already have been translated
|
|
elsewhere. If any of the Sids have been translated, the
|
|
Names parameter will point to a location containing a non-NULL
|
|
array of Name translation structures corresponding to the
|
|
Sids. If the nth Sid has been translated, the nth name
|
|
translation structure will contain either a non-NULL name
|
|
or a non-negative offset into the Referenced Domain List. If
|
|
the nth Sid has not yet been translated, the nth name
|
|
translation structure will contain a zero-length name string
|
|
and a negative value for the Referenced Domain List index.
|
|
|
|
ReferencedDomains - Pointer to a structure in which the list of domains
|
|
used in the translation is maintained. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for each
|
|
translated name, this structure will only contain one component for
|
|
each domain utilized in the translation.
|
|
|
|
TranslatedNames - Pointer to a structure in which the translations to Names
|
|
corresponding to the Sids specified on Sids is maintained. The
|
|
nth entry in this array provides a translation (where known) for the
|
|
nth element in the Sids parameter.
|
|
|
|
MappedCount - Pointer to location in which a count of the Names that
|
|
have been completely translated is maintained.
|
|
|
|
CompletelyUnmappedCount - Pointer to a location in which a count of the
|
|
Names that have not been translated (either partially, by identification
|
|
of a Domain Prefix, or completely) is maintained.
|
|
|
|
Options - Specifies optional actions.
|
|
|
|
LSAP_DB_SEARCH_BUILT_IN_DOMAIN - Search the Built In Domain
|
|
|
|
LSAP_DB_SEARCH_ACCOUNT_DOMAIN - Search the Account Domain
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Sids may remain partially or completely unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
|
|
STATUS_INTERNAL_DB_ERROR - A corruption has been detected in
|
|
the LSA Database.
|
|
|
|
STATUS_INVALID_PARAMETER - Invalid parameter
|
|
|
|
- No handle to the Policy object was provided on a request
|
|
to search the Account Domain.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status,
|
|
SecondaryStatus = STATUS_SUCCESS;
|
|
|
|
LSAPR_TRUST_INFORMATION
|
|
TrustInformation;
|
|
|
|
ULONG
|
|
UpdatedMappedCount = *MappedCount;
|
|
|
|
LSAPR_HANDLE
|
|
TrustedPolicyHandle = NULL;
|
|
|
|
LSAP_WELL_KNOWN_SID_INDEX
|
|
WellKnownSidIndex;
|
|
|
|
PLSAPR_POLICY_ACCOUNT_DOM_INFO
|
|
PolicyAccountDomainInfo = NULL;
|
|
|
|
|
|
//
|
|
// If there are no completely unmapped Sids remaining, return.
|
|
//
|
|
|
|
if (*CompletelyUnmappedCount == (ULONG) 0) {
|
|
|
|
goto LookupSidsInLocalDomainsFinish;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If requested, lookup Sids in the BUILT-IN Domain.
|
|
//
|
|
|
|
if (Options & LSAP_DB_SEARCH_BUILT_IN_DOMAIN) {
|
|
|
|
//
|
|
// Set up the Trust Information structure for this domain.
|
|
//
|
|
|
|
TrustInformation.Sid = LsapBuiltInDomainSid;
|
|
|
|
Status = STATUS_INTERNAL_DB_ERROR;
|
|
|
|
if (!LsapDbLookupIndexWellKnownSid(
|
|
LsapBuiltInDomainSid,
|
|
&WellKnownSidIndex
|
|
)) {
|
|
|
|
goto LookupSidsInLocalDomainsError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Obtain the name of the Built In Domain from the table of
|
|
// Well Known Sids. It suffices to copy the Unicode structures
|
|
// since we do not need a separate copy of the name buffer.
|
|
//
|
|
|
|
TrustInformation.Name = *((PLSAPR_UNICODE_STRING)
|
|
LsapDbWellKnownSidName(WellKnownSidIndex));
|
|
|
|
Status = LsapDbLookupSidsInLocalDomain(
|
|
LSAP_DB_SEARCH_BUILT_IN_DOMAIN,
|
|
Count,
|
|
Sids,
|
|
&TrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedNames,
|
|
&UpdatedMappedCount,
|
|
CompletelyUnmappedCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsInLocalDomainsError;
|
|
}
|
|
|
|
//
|
|
// If all Sids are now mapped or partially mapped, finish.
|
|
//
|
|
|
|
if (*CompletelyUnmappedCount == (ULONG) 0) {
|
|
|
|
goto LookupSidsInLocalDomainsFinish;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If requested, search the Account Domain.
|
|
//
|
|
|
|
if (Options & LSAP_DB_SEARCH_ACCOUNT_DOMAIN) {
|
|
|
|
//
|
|
// The Sid and Name of the Account Domain are both configurable, and
|
|
// we need to obtain them from the Policy Object. Now obtain the
|
|
// Account Domain Sid and Name by querying the appropriate
|
|
// Policy Information Class.
|
|
//
|
|
|
|
Status = LsarQueryInformationPolicy(
|
|
LsapPolicyHandle,
|
|
PolicyAccountDomainInformation,
|
|
(PLSAPR_POLICY_INFORMATION *) &PolicyAccountDomainInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsInLocalDomainsError;
|
|
}
|
|
|
|
//
|
|
// Set up the Trust Information structure for the Account Domain.
|
|
//
|
|
|
|
TrustInformation.Sid = PolicyAccountDomainInfo->DomainSid;
|
|
|
|
RtlCopyMemory(
|
|
&TrustInformation.Name,
|
|
&PolicyAccountDomainInfo->DomainName,
|
|
sizeof (UNICODE_STRING)
|
|
);
|
|
|
|
//
|
|
// Now search the Account Domain for more Sid translations.
|
|
//
|
|
|
|
Status = LsapDbLookupSidsInLocalDomain(
|
|
LSAP_DB_SEARCH_ACCOUNT_DOMAIN,
|
|
Count,
|
|
Sids,
|
|
&TrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedNames,
|
|
&UpdatedMappedCount,
|
|
CompletelyUnmappedCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsInLocalDomainsError;
|
|
}
|
|
}
|
|
|
|
LookupSidsInLocalDomainsFinish:
|
|
|
|
//
|
|
// If necessary, free the Policy Account Domain Info
|
|
//
|
|
|
|
if (PolicyAccountDomainInfo != NULL) {
|
|
|
|
LsaIFree_LSAPR_POLICY_INFORMATION(
|
|
PolicyAccountDomainInformation,
|
|
(PLSAPR_POLICY_INFORMATION) PolicyAccountDomainInfo
|
|
);
|
|
|
|
PolicyAccountDomainInfo = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Return the updated total count of Sids mapped.
|
|
//
|
|
|
|
*MappedCount = UpdatedMappedCount;
|
|
return(Status);
|
|
|
|
LookupSidsInLocalDomainsError:
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
goto LookupSidsInLocalDomainsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupSidsInLocalDomain(
|
|
IN ULONG LocalDomain,
|
|
IN ULONG Count,
|
|
IN PLSAPR_SID *Sids,
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function looks up Sids in a SAM domain on the local system
|
|
attempts to translate them to names.
|
|
|
|
Arguments:
|
|
|
|
LocalDomain - Indicates which local domain to look in. Valid values
|
|
are:
|
|
LSAP_DB_SEARCH_BUILT_IN_DOMAIN
|
|
LSAP_DB_SEARCH_ACCOUNT_DOMAIN
|
|
|
|
Count - Number of Sids in the Sids array, Note that some of these
|
|
may already have been mapped elsewhere, as specified by the
|
|
MappedCount parameter.
|
|
|
|
Sids - Pointer to array of pointers to Sids to be translated.
|
|
Zero or all of the Sids may already have been translated
|
|
elsewhere. If any of the Sids have been translated, the
|
|
Names parameter will point to a location containing a non-NULL
|
|
array of Name translation structures corresponding to the
|
|
Sids. If the nth Sid has been translated, the nth name
|
|
translation structure will contain either a non-NULL name
|
|
or a non-negative offset into the Referenced Domain List. If
|
|
the nth Sid has not yet been translated, the nth name
|
|
translation structure will contain a zero-length name string
|
|
and a negative value for the Referenced Domain List index.
|
|
|
|
TrustInformation - Pointer to Trust Information specifying a Domain Sid
|
|
and Name.
|
|
|
|
ReferencedDomains - Pointer to a structure in which the list of domains
|
|
used in the translation is maintained. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for each
|
|
translated name, this structure will only contain one component for
|
|
each domain utilized in the translation.
|
|
|
|
TranslatedNames - Pointer to a structure in which the translations to Names
|
|
corresponding to the Sids specified on Sids is maintained. The
|
|
nth entry in this array provides a translation (where known) for the
|
|
nth element in the Sids parameter.
|
|
|
|
MappedCount - Pointer to location in which a count of the Names that
|
|
have been completely translated is maintained.
|
|
|
|
CompletelyUnmappedCount - Pointer to a location in which a count of the
|
|
Names that have not been translated (either partially, by identification
|
|
of a Domain Prefix, or completely) is maintained.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Sids may remain partially or completely unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status,
|
|
SecondaryStatus;
|
|
|
|
PLSA_TRANSLATED_NAME
|
|
OutputNames = NULL;
|
|
|
|
SAMPR_HANDLE
|
|
LocalSamDomainHandle = NULL;
|
|
|
|
PLSAPR_UNICODE_STRING
|
|
Names = NULL;
|
|
|
|
PSID_NAME_USE
|
|
Use = NULL;
|
|
|
|
ULONG
|
|
RelativeIdCount,
|
|
RelativeIdIndex,
|
|
SidIndex,
|
|
LocalMappedCount = (ULONG) 0,
|
|
DomainSidCount = (ULONG) 0;
|
|
|
|
LONG
|
|
DomainIndex,
|
|
DomainSidIndexList,
|
|
NextIndex,
|
|
TmpIndex;
|
|
|
|
PULONG
|
|
RelativeIds = NULL,
|
|
SidIndices = NULL;
|
|
|
|
PLSAPR_SID
|
|
DomainSid = TrustInformation->Sid;
|
|
|
|
SAMPR_RETURNED_USTRING_ARRAY
|
|
SamReturnedNames;
|
|
|
|
SAMPR_ULONG_ARRAY
|
|
SamReturnedUses;
|
|
|
|
UCHAR
|
|
SubAuthorityCountDomain;
|
|
|
|
PLSAPR_TRUST_INFORMATION
|
|
FreeTrustInformation = NULL;
|
|
|
|
|
|
|
|
//
|
|
// Make sure the SAM handles have been established.
|
|
//
|
|
|
|
Status = LsapOpenSam();
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
|
|
SamReturnedNames.Count = 0;
|
|
SamReturnedNames.Element = NULL;
|
|
SamReturnedUses.Count = 0;
|
|
SamReturnedUses.Element = NULL;
|
|
|
|
OutputNames = (PLSA_TRANSLATED_NAME) TranslatedNames->Names;
|
|
|
|
SecondaryStatus = STATUS_SUCCESS;
|
|
|
|
if (*MappedCount + *CompletelyUnmappedCount > Count) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto LookupSidsInLocalDomainError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (*CompletelyUnmappedCount == (ULONG) 0) {
|
|
goto LookupSidsInLocalDomainFinish;
|
|
}
|
|
|
|
//
|
|
// Now construct a list of Relative Ids to be looked up. Any Sids that
|
|
// do not belong to the specified domain are ignored. Any Sids that
|
|
// are not marked as having unknown Use are ignored, except for certain
|
|
// Well Known Sids that do not have a Well Known Name. These Sids
|
|
// have known Use and a name string length of 0.
|
|
//
|
|
// First, scan the array of Sids looking for completely unmapped ones
|
|
// that have the same domain prefix as the local domain we are dealing
|
|
// with. Note that we can omit any Sid whose Translated Name entry
|
|
// contains a non-zero DomainIndex since the domain of that Sid has
|
|
// already been identified. Once the number of Sids is known, allocate
|
|
// memory for an array of Relative Ids and a parallel array of indices
|
|
// into the original Sids array. The latter array will be used to locate
|
|
// entries in the TranslatedNames array to which information returned by
|
|
// the SamrLookupIdsInDomain() call will be copied.
|
|
//
|
|
|
|
for (RelativeIdCount = 0, SidIndex = 0, DomainSidIndexList = -1;
|
|
SidIndex < Count;
|
|
SidIndex++) {
|
|
|
|
if ((OutputNames[SidIndex].Use == SidTypeUnknown) &&
|
|
(OutputNames[SidIndex].DomainIndex == LSA_UNKNOWN_INDEX)) {
|
|
|
|
if (LsapRtlPrefixSid( (PSID) DomainSid, (PSID) Sids[SidIndex])) {
|
|
RelativeIdCount++;
|
|
} else if (RtlEqualSid( (PSID)DomainSid, (PSID)Sids[SidIndex])) {
|
|
|
|
//
|
|
// This is the domain sid itself. Update
|
|
// the output information directly, but don't add
|
|
// it to the list of RIDs to be looked up by SAM.
|
|
//
|
|
// NOTE that we don't yet know what our domain index
|
|
// is. So, just link these entries together and we'll
|
|
// set the index later.
|
|
//
|
|
|
|
OutputNames[SidIndex].DomainIndex = DomainSidIndexList;
|
|
DomainSidIndexList = SidIndex;
|
|
OutputNames[SidIndex].Use = SidTypeDomain;
|
|
OutputNames[SidIndex].Name.Buffer = NULL;
|
|
OutputNames[SidIndex].Name.Length = 0;
|
|
OutputNames[SidIndex].Name.MaximumLength = 0;
|
|
|
|
LocalMappedCount++;
|
|
DomainSidCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have any SIDs in this domain, then add it to the
|
|
// referenced domain list.
|
|
//
|
|
|
|
if ((RelativeIdCount != 0) || (DomainSidCount != 0)) {
|
|
|
|
//
|
|
// At least one Sid has the domain Sid as prefix (or is the
|
|
// domain SID). Add the domain to the list of Referenced
|
|
// Domains and obtain a Domain Index back.
|
|
//
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation,
|
|
&DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto LookupSidsInLocalDomainError;
|
|
}
|
|
|
|
//
|
|
// If any of the sids were this domain's sid, then they
|
|
// already have their OutputNames[] entry filled in except
|
|
// that the DomainIndex was unkown. It is now known, so
|
|
// fill it in. Any such entries to change have been linked
|
|
// together using DomainSidIndexList as a listhead.
|
|
//
|
|
|
|
for (NextIndex = DomainSidIndexList;
|
|
NextIndex != -1;
|
|
NextIndex = TmpIndex ) {
|
|
|
|
|
|
TmpIndex = OutputNames[NextIndex].DomainIndex;
|
|
OutputNames[NextIndex].DomainIndex = DomainIndex;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If any of the remaining Sids have the specified Local
|
|
// domain Sid as prefix Sid, look them up
|
|
//
|
|
|
|
if (RelativeIdCount != 0) {
|
|
|
|
//
|
|
// Allocate memory for the Relative Id and cross reference arrays
|
|
//
|
|
|
|
RelativeIds = LsapAllocateLsaHeap( RelativeIdCount * sizeof(ULONG));
|
|
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (RelativeIds == NULL) {
|
|
goto LookupSidsInLocalDomainError;
|
|
}
|
|
|
|
SidIndices = LsapAllocateLsaHeap( RelativeIdCount * sizeof(ULONG));
|
|
|
|
if (SidIndices == NULL) {
|
|
goto LookupSidsInLocalDomainError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Obtain the SubAuthorityCount for the Domain Sid
|
|
//
|
|
|
|
SubAuthorityCountDomain = *RtlSubAuthorityCountSid( (PSID) DomainSid );
|
|
|
|
//
|
|
// Now setup the array of Relative Ids to be looked up, recording
|
|
// in the SidIndices array the index of the corresponding Sid within the
|
|
// original Sids array. Set the DomainIndex field for those Sids
|
|
// eligible for the SAM lookup.
|
|
//
|
|
|
|
for (RelativeIdIndex = 0, SidIndex = 0;
|
|
(RelativeIdIndex < RelativeIdCount) && (SidIndex < Count);
|
|
SidIndex++) {
|
|
|
|
if ((OutputNames[SidIndex].Use == SidTypeUnknown) &&
|
|
(OutputNames[SidIndex].DomainIndex == LSA_UNKNOWN_INDEX)) {
|
|
|
|
if (LsapRtlPrefixSid( (PSID) DomainSid, (PSID) Sids[SidIndex] )) {
|
|
|
|
SidIndices[RelativeIdIndex] = SidIndex;
|
|
RelativeIds[RelativeIdIndex] =
|
|
*RtlSubAuthoritySid(
|
|
(PSID) Sids[SidIndex],
|
|
SubAuthorityCountDomain
|
|
);
|
|
|
|
OutputNames[SidIndex].DomainIndex = DomainIndex;
|
|
RelativeIdIndex++;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lookup the Sids in the specified SAM Domain.
|
|
//
|
|
|
|
if (LocalDomain == LSAP_DB_SEARCH_BUILT_IN_DOMAIN ) {
|
|
LocalSamDomainHandle = LsapBuiltinDomainHandle;
|
|
} else {
|
|
ASSERT(LocalDomain == LSAP_DB_SEARCH_ACCOUNT_DOMAIN);
|
|
LocalSamDomainHandle = LsapAccountDomainHandle;
|
|
}
|
|
|
|
//
|
|
// Call SAM to lookup the Relative Id's
|
|
//
|
|
|
|
Status = SamrLookupIdsInDomain(
|
|
LocalSamDomainHandle,
|
|
RelativeIdCount,
|
|
RelativeIds,
|
|
&SamReturnedNames,
|
|
&SamReturnedUses
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// The only error allowed is STATUS_NONE_MAPPED. Filter this out.
|
|
//
|
|
|
|
if (Status != STATUS_NONE_MAPPED) {
|
|
goto LookupSidsInLocalDomainError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Now copy the information returned from SAM into the output
|
|
// Translated Sids array. As we go, compute a count of the names
|
|
// mapped by SAM.
|
|
//
|
|
|
|
for (RelativeIdIndex = 0;
|
|
RelativeIdIndex < SamReturnedNames.Count;
|
|
RelativeIdIndex++) {
|
|
|
|
SidIndex = SidIndices[RelativeIdIndex];
|
|
|
|
//
|
|
// Copy the Sid Use. If the Sid was mapped by this SAM call, copy
|
|
// its Name and increment the count of Sids mapped by this SAM call.
|
|
// Note that we don't need to set the DomainIndex since we did so
|
|
// earlier.
|
|
//
|
|
|
|
OutputNames[SidIndex].Use = SamReturnedUses.Element[RelativeIdIndex];
|
|
|
|
if (OutputNames[SidIndex].Use != SidTypeUnknown) {
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
NULL,
|
|
&OutputNames[SidIndex].Name,
|
|
(PUNICODE_STRING) &SamReturnedNames.Element[RelativeIdIndex]
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
LocalMappedCount++;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsInLocalDomainError;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Update the Mapped and Completely Unmapped Counts. To the Mapped Count
|
|
// add in the number of Sids that SAM completely identified. From the
|
|
// Completely Unmapped Count subtract the number of Sids presented to
|
|
// Sam, since all of these will be at least partially translated.
|
|
//
|
|
|
|
*MappedCount += LocalMappedCount;
|
|
*CompletelyUnmappedCount -= (RelativeIdCount + DomainSidCount);
|
|
|
|
LookupSidsInLocalDomainFinish:
|
|
|
|
//
|
|
// If necessary, free the Lsa Heap buffer allocated for the RelativeIds
|
|
// and SidIndices arrays.
|
|
//
|
|
|
|
if (RelativeIds != NULL) {
|
|
|
|
LsapFreeLsaHeap( RelativeIds );
|
|
RelativeIds = NULL;
|
|
}
|
|
|
|
if (SidIndices != NULL) {
|
|
|
|
LsapFreeLsaHeap( SidIndices );
|
|
SidIndices = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Names array returned from SAM.
|
|
//
|
|
|
|
if ( SamReturnedNames.Count != 0 ) {
|
|
|
|
SamIFree_SAMPR_RETURNED_USTRING_ARRAY ( &SamReturnedNames );
|
|
SamReturnedNames.Count = 0;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Uses array returned from SAM.
|
|
//
|
|
|
|
if ( SamReturnedUses.Count != 0 ) {
|
|
|
|
SamIFree_SAMPR_ULONG_ARRAY ( &SamReturnedUses );
|
|
SamReturnedUses.Count = 0;
|
|
}
|
|
|
|
|
|
return(Status);
|
|
|
|
LookupSidsInLocalDomainError:
|
|
|
|
//
|
|
// If necessary, free memory for the OutputTrustInformation Domain
|
|
// Name Buffer and Sid Buffer.
|
|
//
|
|
|
|
if (DomainIndex >= 0) {
|
|
|
|
FreeTrustInformation = &ReferencedDomains->Domains[DomainIndex];
|
|
|
|
if (FreeTrustInformation->Sid != NULL) {
|
|
|
|
MIDL_user_free( FreeTrustInformation->Sid );
|
|
FreeTrustInformation->Sid = NULL;
|
|
}
|
|
|
|
if (FreeTrustInformation->Name.Buffer != NULL) {
|
|
|
|
MIDL_user_free( FreeTrustInformation->Name.Buffer );
|
|
FreeTrustInformation->Name.Buffer = NULL;
|
|
FreeTrustInformation->Name.Length = 0;
|
|
FreeTrustInformation->Name.MaximumLength = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Name buffer in each of the OutputNames entries
|
|
// written by this routine and set the Name slot to NULL.
|
|
//
|
|
|
|
for (RelativeIdIndex = 0;
|
|
RelativeIdIndex < SamReturnedNames.Count;
|
|
RelativeIdIndex++) {
|
|
|
|
SidIndex = SidIndices[RelativeIdIndex];
|
|
|
|
if (OutputNames[SidIndex].Name.Buffer != NULL) {
|
|
|
|
MIDL_user_free( OutputNames[SidIndex].Name.Buffer );
|
|
OutputNames[SidIndex].Name.Buffer = NULL;
|
|
OutputNames[SidIndex].Name.Length = 0;
|
|
OutputNames[SidIndex].Name.MaximumLength = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Restore the Use field for each entry we wrote to back to SidType
|
|
// Unknown.
|
|
//
|
|
|
|
for (RelativeIdIndex = 0;
|
|
RelativeIdIndex < SamReturnedNames.Count;
|
|
RelativeIdIndex++) {
|
|
|
|
SidIndex = SidIndices[RelativeIdIndex];
|
|
|
|
if (OutputNames[SidIndex].Name.Buffer != NULL) {
|
|
|
|
MIDL_user_free( OutputNames[SidIndex].Name.Buffer );
|
|
OutputNames[SidIndex].Name.Buffer = NULL;
|
|
OutputNames[SidIndex].Name.Length = 0;
|
|
OutputNames[SidIndex].Name.MaximumLength = 0;
|
|
}
|
|
|
|
OutputNames[SidIndex].Use = SidTypeUnknown;
|
|
OutputNames[SidIndex].DomainIndex = LSA_UNKNOWN_INDEX;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
goto LookupSidsInLocalDomainFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupSidsInPrimaryDomain(
|
|
IN ULONG Count,
|
|
IN PLSAPR_SID *Sids,
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to translate Sids in a Primary Domain. A
|
|
Trusted Domain object must exist for the domain in the local Policy
|
|
Database. This object will be used to access the Domain's list of
|
|
Controllers and one or more callouts will be made to access the LSA
|
|
Policy Databases on these Controllers.
|
|
|
|
Arguments:
|
|
|
|
Count - Number of Sids in the Sids array, Note that some of these
|
|
may already have been mapped elsewhere, as specified by the
|
|
MappedCount parameter.
|
|
|
|
Sids - Pointer to array of pointers to Sids to be translated.
|
|
Zero or all of the Sids may already have been translated
|
|
elsewhere. dd esp la If any of the Sids have been translated, the
|
|
|
|
Names parameter will point to a location containing a non-NULL
|
|
array of Name translation structures corresponding to the
|
|
Sids. If the nth Sid has been translated, the nth name
|
|
translation structure will contain either a non-NULL name
|
|
or a non-negative offset into the Referenced Domain List. If
|
|
the nth Sid has not yet been translated, the nth name
|
|
translation structure will contain a zero-length name string
|
|
and a negative value for the Referenced Domain List index.
|
|
|
|
TrustInformation - Specifies the name and Sid of the Primary Domain.
|
|
|
|
ReferencedDomains - Pointer to a structure in which the list of domains
|
|
used in the translation is maintained. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for each
|
|
translated name, this structure will only contain one component for
|
|
each domain utilized in the translation.
|
|
|
|
TranslatedNames - Pointer to a structure in which the translations to Names
|
|
corresponding to the Sids specified on Sids is maintained. The
|
|
nth entry in this array provides a translation (where known) for the
|
|
nth element in the Sids parameter.
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on this
|
|
machine. Values of this field are are follows:
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Sids or Names are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
NOTE: LsapLookupWksta is not valid for this parameter.
|
|
|
|
MappedCount - Pointer to location in which a count of the Names that
|
|
have been completely translated is maintained.
|
|
|
|
CompletelyUnmappedCount - Pointer to a location in which a count of the
|
|
Names that have not been translated (either partially, by identification
|
|
of a Domain Prefix, or completely) is maintained.
|
|
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Sids may remain partially or completely unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
NTSTATUS SecondaryStatus = STATUS_SUCCESS;
|
|
LSA_HANDLE ControllerPolicyHandle = NULL;
|
|
PLSAPR_UNICODE_STRING ControllerNames = NULL;
|
|
ULONG NextLevelCount;
|
|
ULONG NextLevelMappedCount;
|
|
ULONG SidIndex;
|
|
ULONG NextLevelSidIndex;
|
|
PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
|
|
PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
|
|
PLSA_TRANSLATED_NAME NextLevelNames = NULL;
|
|
PLSAPR_SID *NextLevelSids = NULL;
|
|
LONG FirstEntryIndex;
|
|
PULONG SidIndices = NULL;
|
|
BOOLEAN PartialSidTranslationsAttempted = FALSE;
|
|
|
|
//
|
|
// If there are no completely unmapped Sids remaining, just return.
|
|
//
|
|
|
|
if (*CompletelyUnmappedCount == (ULONG) 0) {
|
|
|
|
goto LookupSidsInPrimaryDomainFinish;
|
|
}
|
|
|
|
//
|
|
// Open the Policy object on some DC for the Primary Domain.
|
|
//
|
|
|
|
Status = LsapDbOpenPolicyTrustedDomain(
|
|
TrustInformation,
|
|
POLICY_VIEW_LOCAL_INFORMATION,
|
|
&ControllerPolicyHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// We cannot access the Primary Domain. Suppress the error
|
|
// and translate Domain Prefix Sids for Sids belonging to
|
|
// the Primary Domain.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto LookupSidsInPrimaryDomainFinish;
|
|
}
|
|
|
|
//
|
|
// We have successfully opened a Domain Controller's Policy
|
|
// Database. Now prepare to hand off a Sid lookup for the
|
|
// remaining unmapped Sids to that Controller. Here, this
|
|
// server side of the LSA is a client of the LSA on the
|
|
// target controller. We will construct an array of the
|
|
// remianing unmapped Sids, look them up and then merge the
|
|
// resulting ReferencedDomains and Translated Names into
|
|
// our existing list.
|
|
//
|
|
|
|
NextLevelCount = *CompletelyUnmappedCount;
|
|
|
|
//
|
|
// Allocate an array to hold the indices of unmapped Sids
|
|
// relative to the original Sids and TranslatedNames->Names
|
|
// arrays.
|
|
//
|
|
|
|
SidIndices = MIDL_user_allocate(NextLevelCount * sizeof(ULONG));
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (SidIndices == NULL) {
|
|
|
|
goto LookupSidsInPrimaryDomainError;
|
|
}
|
|
|
|
//
|
|
// Allocate an array for the Sids to be looked up at the Domain
|
|
// Controller.
|
|
//
|
|
|
|
NextLevelSids = MIDL_user_allocate( sizeof(PSID) * NextLevelCount );
|
|
|
|
if (NextLevelSids == NULL) {
|
|
|
|
goto LookupSidsInPrimaryDomainError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Now scan the original array of Names and its parallel
|
|
// Translated Sids array. Copy over any Sids that are
|
|
// completely unmapped.
|
|
//
|
|
|
|
NextLevelSidIndex = (ULONG) 0;
|
|
|
|
for (SidIndex = 0;
|
|
SidIndex < Count && NextLevelSidIndex < NextLevelCount;
|
|
SidIndex++) {
|
|
|
|
if (LsapDbCompletelyUnmappedName(&TranslatedNames->Names[SidIndex])) {
|
|
|
|
NextLevelSids[NextLevelSidIndex] = Sids[SidIndex];
|
|
SidIndices[NextLevelSidIndex] = SidIndex;
|
|
NextLevelSidIndex++;
|
|
}
|
|
}
|
|
|
|
NextLevelMappedCount = (ULONG) 0;
|
|
|
|
Status = LsaICLookupSids(
|
|
ControllerPolicyHandle,
|
|
NextLevelCount,
|
|
(PSID *) NextLevelSids,
|
|
(PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
|
|
&NextLevelNames,
|
|
LookupLevel,
|
|
&NextLevelMappedCount
|
|
);
|
|
|
|
//
|
|
// If the callout to LsaLookupSids() was unsuccessful, disregard
|
|
// the error and set the domain name for any Sids having this
|
|
// domain Sid as prefix sid. We still want to return translations
|
|
// of Sids we have so far even if we are unable to callout to another
|
|
// LSA.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto LookupSidsInPrimaryDomainFinish;
|
|
}
|
|
|
|
//
|
|
// The callout to LsaLookupSids() was successful. We now have
|
|
// an additional list of Referenced Domains containing the
|
|
// Primary Domain and/or one or more of its Trusted Domains.
|
|
// Merge the two Referenced Domain Lists together, noting that
|
|
// since they are disjoint, the second list is simply
|
|
// concatenated with the first. The index of the first entry
|
|
// of the second list will be used to adjust all of the
|
|
// Domain Index entries in the Translated Names entries.
|
|
// Note that since the memory for the graph of the first
|
|
// Referenced Domain list has been allocated as individual
|
|
// nodes, we specify that the nodes in this graph can be
|
|
// referenced by the output Referenced Domain list.
|
|
//
|
|
|
|
Status = LsapDbLookupMergeDisjointReferencedDomains(
|
|
ReferencedDomains,
|
|
NextLevelReferencedDomains,
|
|
&OutputReferencedDomains,
|
|
LSAP_DB_USE_FIRST_MERGAND_GRAPH
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsInPrimaryDomainError;
|
|
}
|
|
|
|
FirstEntryIndex = ReferencedDomains->Entries;
|
|
|
|
//
|
|
// Now update the original list of Translated Names. We
|
|
// update each entry that has newly been translated by copying
|
|
// the entry from the new list and adjusting its Referenced
|
|
// Domain List Index upwards by adding the index of the first
|
|
// entry in the Next level List..
|
|
//
|
|
|
|
for( NextLevelSidIndex = 0;
|
|
NextLevelSidIndex < NextLevelCount;
|
|
NextLevelSidIndex++ ) {
|
|
|
|
if (!LsapDbCompletelyUnmappedName(&NextLevelNames[NextLevelSidIndex])) {
|
|
|
|
if (NextLevelNames[NextLevelSidIndex].Use != SidTypeUnknown) {
|
|
|
|
SidIndex = SidIndices[NextLevelSidIndex];
|
|
|
|
TranslatedNames->Names[SidIndex].Use
|
|
= NextLevelNames[NextLevelSidIndex].Use;
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
NULL,
|
|
(PUNICODE_STRING) &TranslatedNames->Names[SidIndex].Name,
|
|
&NextLevelNames[NextLevelSidIndex].Name
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
TranslatedNames->Names[SidIndex].DomainIndex =
|
|
FirstEntryIndex +
|
|
NextLevelNames[NextLevelSidIndex].DomainIndex;
|
|
|
|
//
|
|
// Update the count of completely unmapped Sids.
|
|
//
|
|
|
|
(*CompletelyUnmappedCount)--;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsInPrimaryDomainError;
|
|
}
|
|
|
|
//
|
|
// Update the Referenced Domain List if a new one was produced
|
|
// from the merge. We retain the original top-level structure.
|
|
//
|
|
|
|
if (OutputReferencedDomains != NULL) {
|
|
|
|
if (ReferencedDomains->Domains != NULL) {
|
|
|
|
MIDL_user_free( ReferencedDomains->Domains );
|
|
ReferencedDomains->Domains = NULL;
|
|
}
|
|
|
|
*ReferencedDomains = *OutputReferencedDomains;
|
|
MIDL_user_free( OutputReferencedDomains );
|
|
OutputReferencedDomains = NULL;
|
|
}
|
|
|
|
//
|
|
// Update the Mapped Count and close the Controller Policy
|
|
// Handle.
|
|
//
|
|
|
|
*MappedCount += NextLevelMappedCount;
|
|
SecondaryStatus = LsaClose( ControllerPolicyHandle );
|
|
ControllerPolicyHandle = NULL;
|
|
|
|
LookupSidsInPrimaryDomainFinish:
|
|
|
|
//
|
|
// We can return partial translations for Sids that have the specified
|
|
// Domain Sid as Prefix Sid. We do this provided there has been
|
|
// no reported error. Errors resulting from callout to another
|
|
// LSA will have been suppressed.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status) &&
|
|
(*MappedCount < Count) &&
|
|
!PartialSidTranslationsAttempted) {
|
|
|
|
|
|
SecondaryStatus = LsapDbLookupTranslateUnknownSidsInDomain(
|
|
Count,
|
|
Sids,
|
|
TrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedNames,
|
|
LookupLevel,
|
|
MappedCount,
|
|
CompletelyUnmappedCount
|
|
);
|
|
|
|
PartialSidTranslationsAttempted = TRUE;
|
|
|
|
if (!NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
goto LookupSidsInPrimaryDomainError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Next Level Referenced Domain List.
|
|
// Note that this structure is allocated(all_nodes) since it was
|
|
// allocated by the client side of the Domain Controller LSA.
|
|
//
|
|
|
|
if (NextLevelReferencedDomains != NULL) {
|
|
|
|
MIDL_user_free( NextLevelReferencedDomains );
|
|
NextLevelReferencedDomains = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Next Level Sids array. We only free the
|
|
// top level.
|
|
//
|
|
|
|
if (NextLevelSids != NULL) {
|
|
|
|
MIDL_user_free( NextLevelSids );
|
|
NextLevelSids = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Next Level Translated Names array.
|
|
// Note that this array is allocated(all_nodes).
|
|
//
|
|
|
|
if (NextLevelNames != NULL) {
|
|
|
|
MIDL_user_free( NextLevelNames );
|
|
NextLevelNames = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the array that maps Sid Indices from the
|
|
// Next Level to the Current Level.
|
|
//
|
|
|
|
if (SidIndices != NULL) {
|
|
|
|
MIDL_user_free( SidIndices );
|
|
SidIndices = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, close the Controller Policy Handle.
|
|
//
|
|
|
|
if ( ControllerPolicyHandle != NULL) {
|
|
|
|
SecondaryStatus = LsaClose( ControllerPolicyHandle );
|
|
ControllerPolicyHandle = NULL;
|
|
|
|
if (!NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
goto LookupSidsInPrimaryDomainError;
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupSidsInPrimaryDomainError:
|
|
|
|
//
|
|
// If the primary status was a success code, but the secondary
|
|
// status was an error, propagate the secondary status.
|
|
//
|
|
|
|
if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {
|
|
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
goto LookupSidsInPrimaryDomainFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupSidsInTrustedDomains(
|
|
IN ULONG Count,
|
|
IN PLSAPR_SID *Sids,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to lookup Sids to see if they belong to
|
|
any of the Domains that are trusted by the Domain for which this
|
|
machine is a DC.
|
|
|
|
Arguments:
|
|
|
|
Sids - Pointer to an array of Sids to be looked up.
|
|
|
|
Count - Number of Sids in the Sids array, Note that some of these
|
|
may already have been mapped elsewhere, as specified by the
|
|
MappedCount parameter.
|
|
|
|
ReferencedDomains - Pointer to a Referenced Domain List structure.
|
|
The structure references an array of zero or more Trust Information
|
|
entries, one per referenced domain. This array will be appended to
|
|
or reallocated if necessary.
|
|
|
|
TranslatedNames - Pointer to structure that optionally references a list
|
|
of name translations for some of the Sids in the Sids array.
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on this
|
|
machine. Values of this field are are follows:
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Sids or Names are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
NOTE: LsapLookupWksta is not valid for this parameter.
|
|
|
|
MappedCount - Pointer to location containing the number of Sids
|
|
in the Sids array that have already been mapped. This number
|
|
will be updated to reflect additional mapping done by this
|
|
routine.
|
|
|
|
CompletelyUnmappedCount - Pointer to location containing the
|
|
count of completely unmapped Sids. A Sid is completely unmapped
|
|
if it is unknown and also its Domain Prefix Sid is not recognized.
|
|
This count is updated on exit, the number of completely unmapped
|
|
Sids whose Domain Prefices are identified by this routine being
|
|
subtracted from the input value.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that
|
|
some or all of the Sids may remain unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;
|
|
|
|
//
|
|
// Build a WorkList for this Lookup and put it on the Work Queue.
|
|
//
|
|
// NOTE: This routine does not need to hold the Lookup Work Queue
|
|
// lock to ensure validity of the WorkList pointer, because the
|
|
// pointer remains valid until this routine frees it via
|
|
// LsapDbLookupDeleteWorkList(). Although other threads may
|
|
// process the WorkList, do not delete it.
|
|
//
|
|
// A called routine must acquire the lock in order to access
|
|
// the WorkList after it has been added to the Work Queue.
|
|
//
|
|
|
|
Status = LsapDbLookupSidsBuildWorkList(
|
|
Count,
|
|
Sids,
|
|
ReferencedDomains,
|
|
TranslatedNames,
|
|
LookupLevel,
|
|
MappedCount,
|
|
CompletelyUnmappedCount,
|
|
&WorkList
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// If no Work List has been built because there are no
|
|
// eligible domains to search, exit, suppressing the error.
|
|
|
|
if (Status == STATUS_NONE_MAPPED) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto LookupSidsInTrustedDomainsFinish;
|
|
}
|
|
|
|
goto LookupSidsInTrustedDomainsError;
|
|
}
|
|
|
|
//
|
|
// Start the work, by dispatching one or more worker threads
|
|
// if necessary.
|
|
//
|
|
|
|
Status = LsapDbLookupDispatchWorkerThreads( WorkList );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsInTrustedDomainsError;
|
|
}
|
|
|
|
//
|
|
// Wait for completion/termination of all items on the Work List.
|
|
//
|
|
|
|
Status = LsapDbLookupAwaitCompletionWorkList( WorkList );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsInTrustedDomainsError;
|
|
}
|
|
|
|
LookupSidsInTrustedDomainsFinish:
|
|
|
|
//
|
|
// If a Work List was created, delete it from the Work Queue
|
|
//
|
|
|
|
if (WorkList != NULL) {
|
|
|
|
Status = LsapDbLookupDeleteWorkList( WorkList );
|
|
WorkList = NULL;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupSidsInTrustedDomainsError:
|
|
|
|
goto LookupSidsInTrustedDomainsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupNamesInLocalDomains(
|
|
IN ULONG Count,
|
|
IN PLSAPR_UNICODE_STRING Names,
|
|
IN PLSAPR_UNICODE_STRING PrefixNames,
|
|
IN PLSAPR_UNICODE_STRING SuffixNames,
|
|
IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
|
|
IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount,
|
|
IN ULONG Options
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function looks up Names in the local SAM domains and attempts to
|
|
translate them to Sids. Currently, there are two local SAM domains,
|
|
the Built-in domain (which has a well-known Sid and name) and the
|
|
Account Domain (which has a configurable Sid and name).
|
|
|
|
Arguments:
|
|
|
|
Count - Number of Names in the Names array, Note that some of these
|
|
may already have been mapped elsewhere, as specified by the
|
|
MappedCount parameter.
|
|
|
|
Names - Pointer to array of Unicode Names to be translated.
|
|
Zero or all of the Names may already have been translated
|
|
elsewhere. If any of the Names have been translated, the
|
|
TranslatedSids parameter will point to a location containing a non-NULL
|
|
array of Sid translation structures corresponding to the
|
|
Names. If the nth Name has been translated, the nth Sid
|
|
translation structure will contain either a non-NULL RelativeId
|
|
or a non-negative offset into the Referenced Domain List. If
|
|
the nth Name has not yet been translated, the nth Sid
|
|
translation structure will contain SidTypeUnknown in its
|
|
Use field.
|
|
|
|
PrefixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Prefix portions of the Names. Names having no
|
|
Prefix are called Isolated Names. For these, the Unicode String
|
|
structure is set to contain a zero Length.
|
|
|
|
SuffixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Suffix portions of the Names.
|
|
|
|
ReferencedDomains - Pointer to a location containing either NULL
|
|
or a pointer to a Referenced Domain List structure. If an
|
|
existing Referenced Domain List structure is supplied, it
|
|
will be appended/reallocated if necessary.
|
|
|
|
TranslatedSids - Pointer to structure that optionally references a list
|
|
of Sid translations for some of the Names in the Names array.
|
|
|
|
MappedCount - Pointer to location containing the number of Names
|
|
in the Names array that have already been mapped. This number
|
|
will be updated to reflect additional mapping done by this
|
|
routine.
|
|
|
|
CompletelyUnmappedCount - Pointer to location containing the
|
|
count of completely unmapped Sids. A Name is completely unmapped
|
|
if it is unknown and non-composite, or composite but with an
|
|
unrecognized Domain component. This count is updated on exit, the
|
|
number of completely unmapped Namess whose Domain Prefices are
|
|
identified by this routine being subtracted from the input value.
|
|
|
|
Options - Specifies optional actions.
|
|
|
|
LSAP_DB_SEARCH_BUILT_IN_DOMAIN - Search the Built In Domain
|
|
|
|
LSAP_DB_SEARCH_ACCOUNT_DOMAIN - Search the Account Domain
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Names may remain partially or completely unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
|
|
STATUS_INTERNAL_DB_ERROR - A corruption has been detected in
|
|
the LSA Database.
|
|
|
|
STATUS_INVALID_PARAMETER - Invalid parameter
|
|
|
|
- No handle to the Policy object was provided on a request
|
|
to search the Account Domain.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status,
|
|
SecondaryStatus = STATUS_SUCCESS;
|
|
|
|
ULONG
|
|
UpdatedMappedCount = *MappedCount;
|
|
|
|
LSAPR_HANDLE
|
|
TrustedPolicyHandle = NULL;
|
|
|
|
PLSAPR_POLICY_ACCOUNT_DOM_INFO
|
|
PolicyAccountDomainInfo = NULL;
|
|
|
|
|
|
//
|
|
// If there are no completely unmapped Names remaining, return.
|
|
//
|
|
|
|
if (*CompletelyUnmappedCount == (ULONG) 0) {
|
|
|
|
goto LookupNamesInLocalDomainsFinish;
|
|
}
|
|
|
|
|
|
//
|
|
// If requested, lookup Names in the BUILT-IN Domain.
|
|
//
|
|
|
|
if (Options & LSAP_DB_SEARCH_BUILT_IN_DOMAIN) {
|
|
|
|
Status = LsapDbLookupNamesInLocalDomain(
|
|
LSAP_DB_SEARCH_BUILT_IN_DOMAIN,
|
|
Count,
|
|
PrefixNames,
|
|
SuffixNames,
|
|
BuiltInDomainTrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedSids,
|
|
&UpdatedMappedCount,
|
|
CompletelyUnmappedCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesInLocalDomainsError;
|
|
}
|
|
|
|
//
|
|
// If all Names are now mapped or partially mapped, finish.
|
|
//
|
|
|
|
if (*CompletelyUnmappedCount == (ULONG) 0) {
|
|
|
|
goto LookupNamesInLocalDomainsFinish;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If requested, search the Account Domain.
|
|
//
|
|
|
|
if (Options & LSAP_DB_SEARCH_ACCOUNT_DOMAIN) {
|
|
|
|
Status = LsapDbLookupNamesInLocalDomain(
|
|
LSAP_DB_SEARCH_ACCOUNT_DOMAIN,
|
|
Count,
|
|
PrefixNames,
|
|
SuffixNames,
|
|
AccountDomainTrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedSids,
|
|
&UpdatedMappedCount,
|
|
CompletelyUnmappedCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesInLocalDomainsError;
|
|
}
|
|
}
|
|
|
|
LookupNamesInLocalDomainsFinish:
|
|
|
|
|
|
//
|
|
// Return the updated total count of Names mapped.
|
|
//
|
|
|
|
*MappedCount = UpdatedMappedCount;
|
|
return(Status);
|
|
|
|
LookupNamesInLocalDomainsError:
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
goto LookupNamesInLocalDomainsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupNamesInLocalDomain(
|
|
IN ULONG LocalDomain,
|
|
IN ULONG Count,
|
|
IN PLSAPR_UNICODE_STRING PrefixNames,
|
|
IN PLSAPR_UNICODE_STRING SuffixNames,
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function looks up Names in a SAM domain on the local system and
|
|
attempts to translate them to Sids.
|
|
|
|
Arguments:
|
|
|
|
LocalDomain - Indicates which local domain to look in. Valid values
|
|
are:
|
|
LSAP_DB_SEARCH_BUILT_IN_DOMAIN
|
|
LSAP_DB_SEARCH_ACCOUNT_DOMAIN
|
|
|
|
Count - Number of Names in the PrefixNames and SuffixNames arrays,
|
|
Note that some of these may already have been mapped elsewhere, as
|
|
specified by the MappedCount parameter.
|
|
|
|
PrefixNames - Pointer to array of Prefix Names. These are Domain
|
|
Names or NULL Unicode Strings. Only those Names whose Prefix
|
|
Names are NULL or the same as the Domain Name specified in the
|
|
TrustInformation parameter are eligible for the search.
|
|
|
|
SuffixNames - Pointer to array of Terminal Names to be translated.
|
|
Terminal Names are the last component of the name.
|
|
Zero or all of the Names may already have been translated
|
|
elsewhere. If any of the Names have been translated, the
|
|
Sids parameter will point to a location containing a non-NULL
|
|
array of Sid translation structures corresponding to the
|
|
Sids. If the nth Sid has been translated, the nth Sid
|
|
translation structure will contain either a non-NULL Sid
|
|
or a non-negative offset into the Referenced Domain List. If
|
|
the nth Sid has not yet been translated, the nth name
|
|
translation structure will contain a zero-length name string
|
|
and a negative value for the Referenced Domain List index.
|
|
|
|
TrustInformation - Pointer to Trust Information specifying a Domain Sid
|
|
and Name.
|
|
|
|
ReferencedDomains - Pointer to a Referenced Domain List. This
|
|
list references an array of zero or more Trust Information
|
|
entries describing each of the domains referenced by the names.
|
|
This array will be appended to/reallocated if necessary.
|
|
|
|
TranslatedSids - Pointer to location containing NULL or a pointer to a
|
|
array of Sid translation structures.
|
|
|
|
MappedCount - Pointer to location containing the number of Names
|
|
in the Names array that have already been mapped. This number
|
|
will be updated to reflect additional mapping done by this
|
|
routine.
|
|
|
|
CompletelyUnmappedCount - Pointer to location containing the
|
|
count of completely unmapped Sids. A Name is completely unmapped
|
|
if it is unknown and non-composite, or composite but with an
|
|
unrecognized Domain component. This count is updated on exit, the
|
|
number of completely unmapped Namess whose Domain Prefices are
|
|
identified by this routine being subtracted from the input value.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Names may remain partially or completely unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status = STATUS_SUCCESS,
|
|
SecondaryStatus = STATUS_SUCCESS,
|
|
IgnoreStatus;
|
|
|
|
ULONG
|
|
UnmappedNameCount = 0,
|
|
OutputSidsLength,
|
|
NameLookupCount,
|
|
NameLookupIndex,
|
|
SidIndex;
|
|
|
|
LONG
|
|
DomainIndex = LSA_UNKNOWN_INDEX;
|
|
|
|
PULONG
|
|
SidIndices = NULL;
|
|
|
|
PLSA_TRANSLATED_SID
|
|
OutputSids = NULL;
|
|
|
|
SAMPR_HANDLE
|
|
LocalSamDomainHandle = NULL,
|
|
LocalSamUserHandle = NULL;
|
|
|
|
PLSAPR_UNICODE_STRING
|
|
SamLookupSuffixNames = NULL,
|
|
DomainName = &TrustInformation->Name;
|
|
|
|
PLSAPR_SID
|
|
DomainSid = TrustInformation->Sid;
|
|
|
|
SAMPR_ULONG_ARRAY
|
|
SamReturnedIds,
|
|
SamReturnedUses;
|
|
|
|
PLSAPR_TRUST_INFORMATION
|
|
FreeTrustInformation = NULL;
|
|
|
|
PUSER_CONTROL_INFORMATION
|
|
UserControlInfo;
|
|
|
|
SamReturnedIds.Count = 0;
|
|
SamReturnedIds.Element = NULL;
|
|
SamReturnedUses.Count = 0;
|
|
SamReturnedUses.Element = NULL;
|
|
|
|
|
|
//
|
|
// Make sure the SAM handles have been established.
|
|
//
|
|
|
|
Status = LsapOpenSam();
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
|
|
//
|
|
// It is an internal error if the TranslatedSids or ReferencedDomains
|
|
// parameters have not been specified. Further, The TranslatedSids->Sids
|
|
// pointer must be non-NULL.
|
|
//
|
|
|
|
ASSERT(ARGUMENT_PRESENT(TranslatedSids));
|
|
ASSERT(TranslatedSids->Sids != NULL);
|
|
ASSERT(ARGUMENT_PRESENT(ReferencedDomains));
|
|
|
|
//
|
|
// Validate the Count and MappedCount parameters.
|
|
//
|
|
|
|
|
|
if (*MappedCount + *CompletelyUnmappedCount > Count) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto LookupNamesInLocalDomainError;
|
|
}
|
|
|
|
|
|
if (*CompletelyUnmappedCount == (ULONG) 0) {
|
|
|
|
goto LookupNamesInLocalDomainFinish;
|
|
}
|
|
|
|
//
|
|
// Not all of the Names have yet been mapped. We'll try to map the
|
|
// remaining names by searching the designated SAM domain on this
|
|
// machine.
|
|
//
|
|
|
|
UnmappedNameCount = Count - *MappedCount;
|
|
OutputSids = (PLSA_TRANSLATED_SID) TranslatedSids->Sids;
|
|
OutputSidsLength = Count * sizeof (LSA_TRANSLATED_SID);
|
|
|
|
//
|
|
// Allocate memory for array of names to be presented to SAM. Note
|
|
// that, for simplicity, we allocate an array for the maximal case
|
|
// in which all of the reamining unmapped names are eligible
|
|
// for the search in this domain. This avoids having to scan the
|
|
// names array twice, once to compute the number of eligible names.
|
|
//
|
|
|
|
SamLookupSuffixNames = LsapAllocateLsaHeap( UnmappedNameCount * sizeof(UNICODE_STRING));
|
|
|
|
|
|
if (SamLookupSuffixNames == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto LookupNamesInLocalDomainError;
|
|
}
|
|
|
|
//
|
|
// Allocate an array to record indices of eligible names. This is
|
|
// used upon return from SAM to locate the entries in the
|
|
// OutputSids array that are to be updated. For simplicity, we
|
|
// allocate the array with sufficient entries for all of the remaining
|
|
// unmapped names.
|
|
//
|
|
|
|
SidIndices = LsapAllocateLsaHeap( UnmappedNameCount * sizeof(ULONG));
|
|
|
|
if (SidIndices == NULL) {
|
|
|
|
goto LookupNamesInLocalDomainError;
|
|
}
|
|
|
|
|
|
//
|
|
// Scan the output array of Sid translations to locate entries for names
|
|
// that have not yet been mapped. For each unmapped name, check
|
|
// eligibility of the name for the search of this domain.
|
|
//
|
|
// - All isolated names are eligible for the search
|
|
// - All composite names having this domain name as prefix are
|
|
// eligible.
|
|
//
|
|
// Copy in each eligible suffix or isolated name to the SAM buffer.
|
|
//
|
|
|
|
for (NameLookupIndex = 0, SidIndex = 0; SidIndex < Count; SidIndex++) {
|
|
|
|
if (OutputSids[SidIndex].Use == SidTypeUnknown) {
|
|
|
|
//
|
|
// Found a name that has not yet been mapped. Check for a name
|
|
// Prefix. If none has been specified, the whole name may either
|
|
// be NULL, the name of the domain itself or an isolated name.
|
|
//
|
|
|
|
if (PrefixNames[SidIndex].Length == 0) {
|
|
|
|
//
|
|
// Name is isolated. If whole name is NULL, skip.
|
|
//
|
|
|
|
if (SuffixNames[SidIndex].Length == 0) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If name is the name of the domain itself, use the
|
|
// Trust Information to translate it, and fill in the
|
|
// appropriate Translated Sids entry. The name will
|
|
// then be excluded from further searches.
|
|
//
|
|
|
|
if (RtlEqualDomainName(
|
|
(PUNICODE_STRING) &(TrustInformation->Name),
|
|
(PUNICODE_STRING) &SuffixNames[SidIndex]
|
|
)) {
|
|
|
|
|
|
Status = LsapDbLookupTranslateNameDomain(
|
|
TrustInformation,
|
|
&OutputSids[SidIndex],
|
|
ReferencedDomains,
|
|
&DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Name is an isolated name not equal to the domain name and
|
|
// so is eligible for the search. Reference it from the SAM buffer,
|
|
// remember its index and increment the buffer index. The
|
|
// SAM buffer is an IN parameter to a further lookup call.
|
|
//
|
|
|
|
SamLookupSuffixNames[NameLookupIndex] = SuffixNames[SidIndex];
|
|
|
|
|
|
//
|
|
// We should never have an index that equals or exceeds the
|
|
// UnmappedNameCount.
|
|
//
|
|
|
|
ASSERT(NameLookupIndex < UnmappedNameCount);
|
|
|
|
SidIndices[NameLookupIndex] = SidIndex;
|
|
NameLookupIndex++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Name has a non-NULL Prefix Name. Cpmpare the name with the
|
|
// name of the Domain being searched.
|
|
|
|
if (RtlEqualDomainName(
|
|
(PUNICODE_STRING) &TrustInformation->Name,
|
|
(PUNICODE_STRING) &PrefixNames[SidIndex]
|
|
)) {
|
|
|
|
//
|
|
// Prefix name matches the name of the Domain. If the
|
|
// Suffix Name is NULL, the name of the domain itself
|
|
// has been specified (in the form <DomainName> "\").
|
|
//
|
|
|
|
if (SuffixNames[SidIndex].Length == 0) {
|
|
|
|
Status = LsapDbLookupTranslateNameDomain(
|
|
TrustInformation,
|
|
&OutputSids[SidIndex],
|
|
ReferencedDomains,
|
|
&DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Name is composite and the Prefix name matches the name of
|
|
// this domain. We will at least be able to partially translate
|
|
// name, so add this domain to the Referenced Domain List if not
|
|
// already there. Then reference the Suffix Name from the SAM buffer,
|
|
// remember its index and increment the buffer index. The
|
|
// SAM buffer is an IN parameter to a further lookup call.
|
|
//
|
|
|
|
if (DomainIndex == LSA_UNKNOWN_INDEX) {
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation,
|
|
&DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
SamLookupSuffixNames[NameLookupIndex] = SuffixNames[SidIndex];
|
|
|
|
SidIndices[NameLookupIndex] = SidIndex;
|
|
OutputSids[SidIndex].DomainIndex = DomainIndex;
|
|
NameLookupIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesInLocalDomainError;
|
|
}
|
|
|
|
//
|
|
// If none of the remaining Names are eligible for searching in this
|
|
// domain, finish up.
|
|
//
|
|
|
|
NameLookupCount = NameLookupIndex;
|
|
|
|
if (NameLookupCount == 0) {
|
|
|
|
goto LookupNamesInLocalDomainFinish;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Lookup the Sids in the specified SAM Domain.
|
|
//
|
|
|
|
if (LocalDomain == LSAP_DB_SEARCH_BUILT_IN_DOMAIN ) {
|
|
LocalSamDomainHandle = LsapBuiltinDomainHandle;
|
|
} else {
|
|
ASSERT(LocalDomain == LSAP_DB_SEARCH_ACCOUNT_DOMAIN);
|
|
LocalSamDomainHandle = LsapAccountDomainHandle;
|
|
}
|
|
|
|
//
|
|
// Call SAM to lookup the Names in the Domain.
|
|
//
|
|
|
|
Status = SamrLookupNamesInDomain(
|
|
LocalSamDomainHandle,
|
|
NameLookupCount,
|
|
(PRPC_UNICODE_STRING) SamLookupSuffixNames,
|
|
&SamReturnedIds,
|
|
&SamReturnedUses
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// The only error allowed is STATUS_NONE_MAPPED. Filter this out.
|
|
//
|
|
|
|
if (Status != STATUS_NONE_MAPPED) {
|
|
|
|
goto LookupNamesInLocalDomainError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto LookupNamesInLocalDomainFinish;
|
|
}
|
|
|
|
//
|
|
// Filter through the returned Ids to eliminate user accounts that are
|
|
// not marked NORMAL.
|
|
//
|
|
|
|
for (NameLookupIndex = 0;
|
|
NameLookupIndex < SamReturnedIds.Count;
|
|
NameLookupIndex++) {
|
|
|
|
//
|
|
// If this account is a User Account, check its User Control
|
|
// Information. If the account control information indicates
|
|
// that the account is not a Normal User Account, for example
|
|
// it is a machine account, do not return information about
|
|
// the account. Mark it as unknown.
|
|
//
|
|
|
|
if (SamReturnedUses.Element[ NameLookupIndex ] != SidTypeUser) {
|
|
|
|
continue;
|
|
}
|
|
|
|
Status = SamrOpenUser(
|
|
LocalSamDomainHandle,
|
|
USER_READ_ACCOUNT,
|
|
SamReturnedIds.Element[ NameLookupIndex ],
|
|
&LocalSamUserHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
UserControlInfo = NULL;
|
|
|
|
Status = SamrQueryInformationUser(
|
|
LocalSamUserHandle,
|
|
UserControlInformation,
|
|
(PSAMPR_USER_INFO_BUFFER *) &UserControlInfo
|
|
);
|
|
IgnoreStatus = SamrCloseHandle(&LocalSamUserHandle);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
LocalSamUserHandle = NULL;
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
MIDL_user_free( UserControlInfo );
|
|
break;
|
|
}
|
|
|
|
if (!(UserControlInfo->UserAccountControl & USER_NORMAL_ACCOUNT) &&
|
|
!(UserControlInfo->UserAccountControl & USER_TEMP_DUPLICATE_ACCOUNT)) {
|
|
SamReturnedUses.Element[NameLookupIndex] = SidTypeUnknown;
|
|
}
|
|
|
|
MIDL_user_free( UserControlInfo );
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesInLocalDomainError;
|
|
}
|
|
|
|
//
|
|
// SAM found at least one eligible name in the specified domain.
|
|
// Add the domain to the Referenced Domain List.
|
|
//
|
|
|
|
Status = LsapDbLookupTranslateNameDomain(
|
|
TrustInformation,
|
|
NULL,
|
|
ReferencedDomains,
|
|
&DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesInLocalDomainError;
|
|
}
|
|
|
|
//
|
|
// Now copy the information returned from SAM into the output
|
|
// Translated Sids array.
|
|
//
|
|
|
|
for (NameLookupIndex = 0;
|
|
NameLookupIndex < SamReturnedIds.Count;
|
|
NameLookupIndex++) {
|
|
|
|
SidIndex = SidIndices[NameLookupIndex];
|
|
|
|
//
|
|
// If we have newly translated a Name, increment the mapped
|
|
// count and copy information returned by SAM.
|
|
//
|
|
|
|
if ((OutputSids[SidIndex].Use == SidTypeUnknown) &&
|
|
SamReturnedUses.Element[NameLookupIndex] != (ULONG) SidTypeUnknown) {
|
|
|
|
(*MappedCount)++;
|
|
OutputSids[SidIndex].Use = (SID_NAME_USE) SamReturnedUses.Element[NameLookupIndex];
|
|
OutputSids[SidIndex].RelativeId = SamReturnedIds.Element[NameLookupIndex];
|
|
OutputSids[SidIndex].DomainIndex = DomainIndex;
|
|
}
|
|
}
|
|
|
|
LookupNamesInLocalDomainFinish:
|
|
|
|
//
|
|
// If successful, update count of completely unmapped names provided.
|
|
// Note that STATUS_NONE_MAPPED errors are suppressed before we get here.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
LsapDbUpdateCountCompUnmappedNames(TranslatedSids, CompletelyUnmappedCount);
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Lsa Heap buffer allocated for the
|
|
// SamLookupSuffixNames and SidIndices arrays.
|
|
//
|
|
|
|
if (SamLookupSuffixNames != NULL) {
|
|
LsapFreeLsaHeap( SamLookupSuffixNames );
|
|
}
|
|
|
|
if (SidIndices != NULL) {
|
|
LsapFreeLsaHeap( SidIndices );
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Relative Ids array returned from SAM.
|
|
//
|
|
|
|
if ( SamReturnedIds.Count != 0 ) {
|
|
SamIFree_SAMPR_ULONG_ARRAY ( &SamReturnedIds );
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Uses array returned from SAM.
|
|
//
|
|
|
|
if ( SamReturnedUses.Count != 0 ) {
|
|
SamIFree_SAMPR_ULONG_ARRAY ( &SamReturnedUses );
|
|
}
|
|
|
|
|
|
return(Status);
|
|
|
|
LookupNamesInLocalDomainError:
|
|
|
|
//
|
|
// If necessary, free memory for the OutputTrustInformation Domain
|
|
// Name Buffer and Sid Buffer.
|
|
//
|
|
|
|
if (DomainIndex >= 0) {
|
|
|
|
FreeTrustInformation = &ReferencedDomains->Domains[DomainIndex];
|
|
|
|
if (FreeTrustInformation->Sid != NULL) {
|
|
|
|
MIDL_user_free( FreeTrustInformation->Sid );
|
|
FreeTrustInformation->Sid = NULL;
|
|
}
|
|
|
|
if (FreeTrustInformation->Name.Buffer != NULL) {
|
|
|
|
MIDL_user_free( FreeTrustInformation->Name.Buffer );
|
|
FreeTrustInformation->Name.Buffer = NULL;
|
|
FreeTrustInformation->Name.Length = 0;
|
|
FreeTrustInformation->Name.MaximumLength = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset the Use field for each of the entries written to back to
|
|
// SidTypeUnknown and set the DomainIndex back to LSA_UNKNOWN_INDEX.
|
|
//
|
|
|
|
for (NameLookupIndex = 0;
|
|
NameLookupIndex < SamReturnedIds.Count;
|
|
NameLookupIndex++) {
|
|
|
|
SidIndex = SidIndices[NameLookupIndex];
|
|
OutputSids[SidIndex].Use = SidTypeUnknown;
|
|
OutputSids[SidIndex].DomainIndex = LSA_UNKNOWN_INDEX;
|
|
}
|
|
|
|
|
|
//
|
|
// If the last User Account handle is still open, close it..
|
|
//
|
|
|
|
if (LocalSamUserHandle != NULL) {
|
|
SecondaryStatus = SamrCloseHandle(&LocalSamUserHandle);
|
|
LocalSamUserHandle = NULL;
|
|
}
|
|
|
|
goto LookupNamesInLocalDomainFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupNamesInPrimaryDomain(
|
|
IN ULONG Count,
|
|
IN PLSAPR_UNICODE_STRING Names,
|
|
IN PLSAPR_UNICODE_STRING PrefixNames,
|
|
IN PLSAPR_UNICODE_STRING SuffixNames,
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to translate Names by searching the Primary
|
|
Domain.
|
|
|
|
Arguments:
|
|
|
|
Count - Number of Names in the Names array, Note that some of these
|
|
may already have been mapped elsewhere, as specified by the
|
|
MappedCount parameter.
|
|
|
|
Names - Pointer to array of Names to be translated.
|
|
Zero or all of the Names may already have been translated
|
|
elsewhere. If any of the Names have been translated, the
|
|
TranslatedSids parameter will point to a location containing a non-NULL
|
|
array of Sid translation structures corresponding to the
|
|
Names. If the nth Name has been translated, the nth Sid
|
|
translation structure will contain either a non-NULL Sid
|
|
or a non-negative offset into the Referenced Domain List. If
|
|
the nth Name has not yet been translated, the nth Sid
|
|
translation structure will contain a zero-length Sid string
|
|
and a negative value for the Referenced Domain List index.
|
|
|
|
PrefixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Prefix portions of the Names. Names having no
|
|
Prefix are called Isolated Names. For these, the Unicode String
|
|
structure is set to contain a zero Length.
|
|
|
|
SuffixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Suffix portions of the Names.
|
|
|
|
TrustInformation - Specifies the name and Sid of the Primary Domain.
|
|
|
|
ReferencedDomains - Pointer to a Referenced Domain List. This
|
|
list references an array of zero or more Trust Information
|
|
entries describing each of the domains referenced by the names.
|
|
This array will be appended to/reallocated if necessary.
|
|
|
|
TranslatedSids - Pointer to structure that optionally references a list
|
|
of Sid translations for some of the Names in the Names array.
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on this
|
|
machine. Values of this field are are follows:
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Names are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
NOTE: LsapLookupWksta is not valid for this parameter.
|
|
|
|
MappedCount - Pointer to location containing the number of Names
|
|
in the Names array that have already been mapped. This number
|
|
will be updated to reflect additional mapping done by this
|
|
routine.
|
|
|
|
CompletelyUnmappedCount - Pointer to location containing the
|
|
count of completely unmapped Names. A Name is completely unmapped
|
|
if it is unknown and non-composite, or composite but with an
|
|
unrecognized Domain component. This count is updated on exit, the
|
|
number of completely unmapped Namess whose Domain Prefices are
|
|
identified by this routine being subtracted from the input value.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Names may remain partially or completely unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS SecondaryStatus = STATUS_SUCCESS;
|
|
LSA_HANDLE ControllerPolicyHandle = NULL;
|
|
ULONG NextLevelCount;
|
|
ULONG NextLevelMappedCount;
|
|
ULONG NameIndex;
|
|
ULONG NextLevelNameIndex;
|
|
PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
|
|
PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
|
|
PLSA_TRANSLATED_SID NextLevelSids = NULL;
|
|
PLSAPR_UNICODE_STRING NextLevelNames = NULL;
|
|
LONG FirstEntryIndex;
|
|
PULONG NameIndices = NULL;
|
|
BOOLEAN PartialNameTranslationsAttempted = FALSE;
|
|
|
|
//
|
|
// If there are no completely unmapped Names remaining, just return.
|
|
//
|
|
|
|
if (*CompletelyUnmappedCount == (ULONG) 0) {
|
|
|
|
goto LookupNamesInPrimaryDomainFinish;
|
|
}
|
|
|
|
//
|
|
// Open the Policy object on some DC for the Primary Domain.
|
|
//
|
|
|
|
Status = LsapDbOpenPolicyTrustedDomain(
|
|
TrustInformation,
|
|
POLICY_VIEW_LOCAL_INFORMATION,
|
|
&ControllerPolicyHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// We cannot access the Primary Domain. Suppress the error
|
|
// and translate Domain Prefix Sids for Sids belonging to
|
|
// the Primary Domain.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto LookupNamesInPrimaryDomainFinish;
|
|
}
|
|
|
|
//
|
|
// We have successfully opened a Domain Controller's Policy
|
|
// Database. Now prepare to hand off a Name lookup for the
|
|
// remaining unmapped Names to that Controller. Here, this
|
|
// server side of the LSA is a client of the LSA on the
|
|
// target controller. We will construct an array of the
|
|
// remianing unmapped Names, look them up and then merge the
|
|
// resulting ReferencedDomains and Translated Sids into
|
|
// our existing list.
|
|
//
|
|
|
|
NextLevelCount = *CompletelyUnmappedCount;
|
|
|
|
//
|
|
// Allocate an array to hold the indices of unmapped Names
|
|
// relative to the original Names and TranslatedSids->Sids
|
|
// arrays.
|
|
//
|
|
|
|
NameIndices = MIDL_user_allocate(NextLevelCount * sizeof(ULONG));
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (NameIndices == NULL) {
|
|
|
|
goto LookupNamesInPrimaryDomainError;
|
|
}
|
|
|
|
//
|
|
// Allocate an array of UNICODE_STRING structures for the
|
|
// names to be looked up at the Domain Controller.
|
|
//
|
|
|
|
NextLevelNames = MIDL_user_allocate(
|
|
sizeof(UNICODE_STRING) * NextLevelCount
|
|
);
|
|
|
|
if (NextLevelNames == NULL) {
|
|
|
|
goto LookupNamesInPrimaryDomainError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Now scan the original array of Names and its parallel
|
|
// Translated Sids array. Copy over any names that are completely
|
|
// unmapped.
|
|
//
|
|
|
|
NextLevelNameIndex = (ULONG) 0;
|
|
|
|
for (NameIndex = 0;
|
|
NameIndex < Count && NextLevelNameIndex < NextLevelCount;
|
|
NameIndex++) {
|
|
|
|
if (LsapDbCompletelyUnmappedSid(&TranslatedSids->Sids[NameIndex])) {
|
|
|
|
NextLevelNames[NextLevelNameIndex] = Names[NameIndex];
|
|
NameIndices[NextLevelNameIndex] = NameIndex;
|
|
NextLevelNameIndex++;
|
|
}
|
|
}
|
|
|
|
NextLevelMappedCount = (ULONG) 0;
|
|
|
|
Status = LsaICLookupNames(
|
|
ControllerPolicyHandle,
|
|
NextLevelCount,
|
|
(PUNICODE_STRING) NextLevelNames,
|
|
(PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
|
|
(PLSA_TRANSLATED_SID *) &NextLevelSids,
|
|
LookupLevel,
|
|
&NextLevelMappedCount
|
|
);
|
|
|
|
//
|
|
// If the callout to LsaLookupNames() was unsuccessful, disregard
|
|
// the error and set the domain name for any Sids having this
|
|
// domain Sid as prefix sid.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto LookupNamesInPrimaryDomainFinish;
|
|
}
|
|
|
|
//
|
|
// The callout to LsaLookupNames() was successful. We now have
|
|
// an additional list of Referenced Domains containing the
|
|
// Primary Domain and/or one or more of its Trusted Domains.
|
|
// Merge the two Referenced Domain Lists together, noting that
|
|
// since they are disjoint, the second list is simply
|
|
// concatenated with the first. The index of the first entry
|
|
// of the second list will be used to adjust all of the
|
|
// Domain Index entries in the Translated Names entries.
|
|
// Note that since the memory for the graph of the first
|
|
// Referenced Domain list has been allocated as individual
|
|
// nodes, we specify that the nodes in this graph can be
|
|
// referenced by the output Referenced Domain list.
|
|
//
|
|
|
|
Status = LsapDbLookupMergeDisjointReferencedDomains(
|
|
ReferencedDomains,
|
|
NextLevelReferencedDomains,
|
|
&OutputReferencedDomains,
|
|
LSAP_DB_USE_FIRST_MERGAND_GRAPH
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesInPrimaryDomainError;
|
|
}
|
|
|
|
FirstEntryIndex = ReferencedDomains->Entries;
|
|
|
|
//
|
|
// Now update the original list of Translated Names. We
|
|
// update each entry that has newly been translated by copying
|
|
// the entry from the new list and adjusting its
|
|
// Referenced Domain List Index upwards by adding the index
|
|
// of the first entry in the Next level List..
|
|
//
|
|
|
|
for( NextLevelNameIndex = 0;
|
|
NextLevelNameIndex < NextLevelCount;
|
|
NextLevelNameIndex++ ) {
|
|
|
|
if (!LsapDbCompletelyUnmappedSid(&NextLevelSids[NextLevelNameIndex])) {
|
|
|
|
NameIndex = NameIndices[NextLevelNameIndex];
|
|
|
|
TranslatedSids->Sids[NameIndex]
|
|
= NextLevelSids[NextLevelNameIndex];
|
|
|
|
TranslatedSids->Sids[NameIndex].DomainIndex =
|
|
FirstEntryIndex +
|
|
NextLevelSids[NextLevelNameIndex].DomainIndex;
|
|
|
|
(*CompletelyUnmappedCount)--;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the Referenced Domain List if a new one was produced
|
|
// from the merge. We retain the original top-level structure.
|
|
//
|
|
|
|
if (OutputReferencedDomains != NULL) {
|
|
|
|
if (ReferencedDomains->Domains != NULL) {
|
|
|
|
MIDL_user_free( ReferencedDomains->Domains );
|
|
ReferencedDomains->Domains = NULL;
|
|
}
|
|
|
|
*ReferencedDomains = *OutputReferencedDomains;
|
|
MIDL_user_free( OutputReferencedDomains );
|
|
OutputReferencedDomains = NULL;
|
|
}
|
|
|
|
//
|
|
// Update the Mapped Count and close the Controller Policy
|
|
// Handle.
|
|
//
|
|
|
|
*MappedCount += NextLevelMappedCount;
|
|
SecondaryStatus = LsaClose( ControllerPolicyHandle );
|
|
ControllerPolicyHandle = NULL;
|
|
|
|
//
|
|
// Any error status that has not been suppressed must be reported
|
|
// to the caller. Errors such as connection failures to other LSA's
|
|
// are suppressed.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesInPrimaryDomainError;
|
|
}
|
|
|
|
LookupNamesInPrimaryDomainFinish:
|
|
|
|
//
|
|
// If necessary, update count of completely unmapped names.
|
|
//
|
|
|
|
if (*CompletelyUnmappedCount > (ULONG) 0) {
|
|
|
|
LsapDbUpdateCountCompUnmappedNames(TranslatedSids, CompletelyUnmappedCount);
|
|
}
|
|
|
|
//
|
|
// We can return partial translations for composite Names in which the
|
|
// domain component is known. We do this provided there has been
|
|
// no reported error. Errors resulting from callout to another
|
|
// LSA will have been suppressed.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status) &&
|
|
(*MappedCount < Count) &&
|
|
!PartialNameTranslationsAttempted) {
|
|
|
|
SecondaryStatus = LsapDbLookupTranslateUnknownNamesInDomain(
|
|
Count,
|
|
Names,
|
|
PrefixNames,
|
|
SuffixNames,
|
|
TrustInformation,
|
|
ReferencedDomains,
|
|
TranslatedSids,
|
|
LookupLevel,
|
|
MappedCount,
|
|
CompletelyUnmappedCount
|
|
);
|
|
|
|
PartialNameTranslationsAttempted = TRUE;
|
|
|
|
if (!NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
goto LookupNamesInPrimaryDomainError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Next Level Referenced Domain List.
|
|
// Note that this structure is allocated(all_nodes) since it was
|
|
// allocated by the client side of the Domain Controller LSA.
|
|
//
|
|
|
|
if (NextLevelReferencedDomains != NULL) {
|
|
|
|
MIDL_user_free( NextLevelReferencedDomains );
|
|
NextLevelReferencedDomains = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Next Level Names array. We only free the
|
|
// top level, since the names therein were copied from the input
|
|
// TranslatedNames->Names array.
|
|
//
|
|
|
|
if (NextLevelNames != NULL) {
|
|
|
|
MIDL_user_free( NextLevelNames );
|
|
NextLevelNames = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the Next Level Translated Sids array. Note
|
|
// that this array is allocated(all_nodes).
|
|
//
|
|
|
|
if (NextLevelSids != NULL) {
|
|
|
|
MIDL_user_free( NextLevelSids );
|
|
NextLevelSids = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the array that maps Name Indices from the
|
|
// Next Level to the Current Level.
|
|
//
|
|
|
|
if (NameIndices != NULL) {
|
|
|
|
MIDL_user_free( NameIndices );
|
|
NameIndices = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, close the Controller Policy Handle.
|
|
//
|
|
|
|
if ( ControllerPolicyHandle != NULL) {
|
|
|
|
SecondaryStatus = LsaClose( ControllerPolicyHandle );
|
|
ControllerPolicyHandle = NULL;
|
|
|
|
if (!NT_SUCCESS(SecondaryStatus)) {
|
|
|
|
goto LookupNamesInPrimaryDomainError;
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupNamesInPrimaryDomainError:
|
|
|
|
//
|
|
// If the primary status was a success code, but the secondary
|
|
// status was an error, propagate the secondary status.
|
|
//
|
|
|
|
if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {
|
|
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
goto LookupNamesInPrimaryDomainFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupNamesInTrustedDomains(
|
|
IN ULONG Count,
|
|
IN PLSAPR_UNICODE_STRING Names,
|
|
IN PLSAPR_UNICODE_STRING PrefixNames,
|
|
IN PLSAPR_UNICODE_STRING SuffixNames,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to lookup Names to see if they belong to
|
|
any of the Domains that are trusted by the Domain for which this
|
|
machine is a DC.
|
|
|
|
Arguments:
|
|
|
|
Count - Number of Names in the Names array, Note that some of these
|
|
may already have been mapped elsewhere, as specified by the
|
|
MappedCount parameter.
|
|
|
|
Names - Pointer to array of Names to be translated. Zero or all of the
|
|
Names may already have been translated elsewhere. If any of the
|
|
Names have been translated, the TranslatedSids parameter will point
|
|
to a location containing a non-NULL array of Sid translation
|
|
structures corresponding to the Names. If the nth Name has been
|
|
translated, the nth Sid translation structure will contain either a
|
|
non-NULL Sid or a non-negative offset into the Referenced Domain
|
|
List. If the nth Name has not yet been translated, the nth Sid
|
|
translation structure will contain a zero-length Sid string and a
|
|
negative value for the Referenced Domain List index.
|
|
|
|
PrefixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Prefix portions of the Names. Names having no
|
|
Prefix are called Isolated Names. For these, the Unicode String
|
|
structure is set to contain a zero Length.
|
|
|
|
SuffixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Suffix portions of the Names.
|
|
|
|
ReferencedDomains - Pointer to a Referenced Domain List. This
|
|
list references an array of zero or more Trust Information
|
|
entries describing each of the domains referenced by the names.
|
|
This array will be appended to/reallocated if necessary.
|
|
|
|
TranslatedSids - Pointer to structure that optionally references a list
|
|
of Sid translations for some of the Names in the Names array.
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on this
|
|
machine. Values of this field are are follows:
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Names are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
NOTE: LsapLookupWksta is not valid for this parameter.
|
|
|
|
MappedCount - Pointer to location containing the number of Names
|
|
in the Names array that have already been mapped. This number
|
|
will be updated to reflect additional mapping done by this
|
|
routine.
|
|
|
|
CompletelyUnmappedCount - Pointer to location containing the
|
|
count of completely unmapped Names. A Name is completely unmapped
|
|
if it is unknown and non-composite, or composite but with an
|
|
unrecognized Domain component. This count is updated on exit, the
|
|
number of completely unmapped Namess whose Domain Prefices are
|
|
identified by this routine being subtracted from the input value.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Names may remain partially or completely unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;
|
|
|
|
//
|
|
// Build a WorkList for this Lookup and put it on the Work Queue.
|
|
//
|
|
// NOTE: This routine does not need to hold the Lookup Work Queue
|
|
// lock to ensure validity of the WorkList pointer, because the
|
|
// pointer remains valid until this routine frees it via
|
|
// LsapDbLookupDeleteWorkList(). Although other threads may
|
|
// process the WorkList, do not delete it.
|
|
//
|
|
// A called routine must acquire the lock in order to access
|
|
// the WorkList after it has been added to the Work Queue.
|
|
//
|
|
|
|
Status = LsapDbLookupNamesBuildWorkList(
|
|
Count,
|
|
Names,
|
|
PrefixNames,
|
|
SuffixNames,
|
|
ReferencedDomains,
|
|
TranslatedSids,
|
|
LookupLevel,
|
|
MappedCount,
|
|
CompletelyUnmappedCount,
|
|
&WorkList
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// If no Work List has been built because there are no
|
|
// eligible domains to search, exit, suppressing the error.
|
|
|
|
if (Status == STATUS_NONE_MAPPED) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto LookupNamesInTrustedDomainsFinish;
|
|
}
|
|
|
|
goto LookupNamesInTrustedDomainsError;
|
|
}
|
|
|
|
//
|
|
// Start the work, by dispatching one or more worker threads
|
|
// if necessary.
|
|
//
|
|
|
|
Status = LsapDbLookupDispatchWorkerThreads( WorkList );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesInTrustedDomainsError;
|
|
}
|
|
|
|
//
|
|
// Wait for completion/termination of all items on the Work List.
|
|
//
|
|
|
|
Status = LsapDbLookupAwaitCompletionWorkList( WorkList );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesInTrustedDomainsError;
|
|
}
|
|
|
|
LookupNamesInTrustedDomainsFinish:
|
|
|
|
//
|
|
// If a Work List was created, delete it from the Work Queue
|
|
//
|
|
|
|
if (WorkList != NULL) {
|
|
|
|
Status = LsapDbLookupDeleteWorkList( WorkList );
|
|
WorkList = NULL;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupNamesInTrustedDomainsError:
|
|
|
|
goto LookupNamesInTrustedDomainsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupTranslateNameDomain(
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN OPTIONAL PLSA_TRANSLATED_SID TranslatedSid,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
OUT PLONG DomainIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function optionally produces a Translated Sid entry for Domain
|
|
from its Name and Sid, and adds Trust Information for the Domain to
|
|
a Referenced Domain List. The index of the new (or existing) entry
|
|
in the Referenced Domain List is returned.
|
|
|
|
Arguments:
|
|
|
|
TrustInformation - Pointer to Trust Information for the Domain consisting
|
|
of its Name and Sid.
|
|
|
|
TranslatedSid - Optional pointer to Translated Sid entry which will
|
|
be filled in with the translation for this Domain. If NULL
|
|
is specified, no entry is fiiled in.
|
|
|
|
ReferencedDomains - Pointer to Referenced Domain List in which an
|
|
entry consisting of the Trust Information for the Domain will be
|
|
made if one does not already exist.
|
|
|
|
DomainIndex - Receives the index of the existing or new entry for the
|
|
Domain in the Referenced Domain List.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation,
|
|
DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto TranslateNameDomainError;
|
|
}
|
|
|
|
//
|
|
// If requested, fill in a Sid translation entry for the domain.
|
|
//
|
|
|
|
if (TranslatedSid != NULL) {
|
|
|
|
TranslatedSid->Use = SidTypeDomain;
|
|
TranslatedSid->RelativeId = LSA_UNKNOWN_ID;
|
|
TranslatedSid->DomainIndex = *DomainIndex;
|
|
}
|
|
|
|
TranslateNameDomainFinish:
|
|
|
|
return(Status);
|
|
|
|
TranslateNameDomainError:
|
|
|
|
goto TranslateNameDomainFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupTranslateUnknownSidsInDomain(
|
|
IN ULONG Count,
|
|
IN PLSAPR_SID *Sids,
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function looks among the unknown Sids in the given list and
|
|
translates the Domain Name for any whose Domain Prefix Sid matches
|
|
the given Domain Sid.
|
|
|
|
Arguments:
|
|
|
|
Count - Number of Sids in the Sids array, Note that some of these
|
|
may already have been mapped elsewhere, as specified by the
|
|
MappedCount parameter.
|
|
|
|
Sids - Pointer to array of pointers to Sids to be translated.
|
|
Zero or all of the Sids may already have been translated
|
|
elsewhere. If any of the Sids have been translated, the
|
|
Names parameter will point to a location containing a non-NULL
|
|
array of Name translation structures corresponding to the
|
|
Sids. If the nth Sid has been translated, the nth name
|
|
translation structure will contain either a non-NULL name
|
|
or a non-negative offset into the Referenced Domain List. If
|
|
the nth Sid has not yet been translated, the nth name
|
|
translation structure will contain a zero-length name string
|
|
and a negative value for the Referenced Domain List index.
|
|
|
|
TrustInformation - Pointer to Trust Information specifying a Domain Sid
|
|
and Name.
|
|
|
|
ReferencedDomains - Pointer to a Referenced Domain List structure.
|
|
The structure references an array of zero or more Trust Information
|
|
entries, one per referenced domain. This array will be appended to
|
|
or reallocated if necessary.
|
|
|
|
TranslatedNames - Pointer to structure that optionally references a list
|
|
of name translations for some of the Sids in the Sids array.
|
|
|
|
MappedCount - Pointer to location containing the number of Sids
|
|
in the Sids array that have already been mapped. This number
|
|
will be updated to reflect additional mapping done by this
|
|
routine.
|
|
|
|
CompletelyUnmappedCount - Pointer to location containing the
|
|
count of completely unmapped Sids. A Sid is completely unmapped
|
|
if it is unknown and also its Domain Prefix Sid is not recognized.
|
|
This count is updated on exit, the number of completely unmapped
|
|
Sids whose Domain Prefices are identified by this routine being
|
|
subtracted from the input value.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Sids may remain partially or completely unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG RemainingCompletelyUnmappedCount;
|
|
ULONG SidIndex;
|
|
PSID DomainSid = TrustInformation->Sid;
|
|
BOOLEAN DomainAlreadyAdded = FALSE;
|
|
LONG DomainIndex;
|
|
|
|
//
|
|
// Scan the array of Sids looking for ones whose domain has not been
|
|
// found.
|
|
//
|
|
|
|
for( SidIndex = 0,
|
|
RemainingCompletelyUnmappedCount = *CompletelyUnmappedCount;
|
|
(RemainingCompletelyUnmappedCount > 0) && (SidIndex < Count);
|
|
SidIndex++) {
|
|
|
|
//
|
|
// Check if this Sid is completely unmapped (i.e. its domain
|
|
// has not yet been identified.
|
|
//
|
|
|
|
if (LsapDbCompletelyUnmappedName(&TranslatedNames->Names[SidIndex])) {
|
|
|
|
//
|
|
// Found a completely unmapped Sid. If it belongs to the
|
|
// specified Domain, add the Domain to the Referenced Domain
|
|
// list if we have not already done so.
|
|
//
|
|
|
|
if (LsapRtlPrefixSid( DomainSid, (PSID) Sids[SidIndex])) {
|
|
|
|
if (!DomainAlreadyAdded) {
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation,
|
|
&DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
DomainAlreadyAdded = TRUE;
|
|
}
|
|
|
|
//
|
|
// Reference the domain from the TranslatedNames entry
|
|
//
|
|
|
|
TranslatedNames->Names[SidIndex].DomainIndex = DomainIndex;
|
|
|
|
//
|
|
// This Sid is now partially translated, so reduce the
|
|
// count of completely unmapped Sids.
|
|
//
|
|
|
|
(*CompletelyUnmappedCount)--;
|
|
}
|
|
|
|
//
|
|
// Decrement count of completely unmapped Sids scanned.
|
|
//
|
|
|
|
RemainingCompletelyUnmappedCount--;
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupTranslateUnknownNamesInDomain(
|
|
IN ULONG Count,
|
|
IN PLSAPR_UNICODE_STRING Names,
|
|
IN PLSAPR_UNICODE_STRING PrefixNames,
|
|
IN PLSAPR_UNICODE_STRING SuffixNames,
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function looks among the unknown Sids in the given list and
|
|
translates the Domain Name for any whose Domain Prefix Sid matches
|
|
the given Domain Sid.
|
|
|
|
Arguments:
|
|
|
|
Count - Number of Names in the Names array, Note that some of these
|
|
may already have been mapped elsewhere, as specified by the
|
|
MappedCount parameter.
|
|
|
|
Names - Pointer to array of Names to be translated.
|
|
Zero or all of the Names may already have been translated
|
|
elsewhere. If any of the Names have been translated, the
|
|
TranslatedSids parameter will point to a location containing a non-NULL
|
|
array of Sid translation structures corresponding to the
|
|
Names. If the nth Name has been translated, the nth Sid
|
|
translation structure will contain either a non-NULL Sid
|
|
or a non-negative offset into the Referenced Domain List. If
|
|
the nth Name has not yet been translated, the nth Sid
|
|
translation structure will contain a zero-length Sid string
|
|
and a negative value for the Referenced Domain List index.
|
|
|
|
PrefixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Prefix portions of the Names. Names having no
|
|
Prefix are called Isolated Names. For these, the Unicode String
|
|
structure is set to contain a zero Length.
|
|
|
|
SuffixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Suffix portions of the Names.
|
|
|
|
TrustInformation - Pointer to Trust Information specifying a Domain Sid
|
|
and Name.
|
|
|
|
ReferencedDomains - Pointer to a Referenced Domain List. This
|
|
list references an array of zero or more Trust Information
|
|
entries describing each of the domains referenced by the names.
|
|
This array will be appended to/reallocated if necessary.
|
|
|
|
TranslatedSids - Pointer to structure that optionally references a list
|
|
of Sid translations for some of the Names in the Names array.
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on this
|
|
machine. Values of this field are are follows:
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Names are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
NOTE: LsapLookupWksta is not valid for this parameter.
|
|
|
|
MappedCount - Pointer to location containing the number of Names
|
|
in the Names array that have already been mapped. This number
|
|
will be updated to reflect additional mapping done by this
|
|
routine.
|
|
|
|
CompletelyUnmappedCount - Pointer to location containing the
|
|
count of completely unmapped Names. A Name is completely unmapped
|
|
if it is unknown and non-composite, or composite but with an
|
|
unrecognized Domain component. This count is updated on exit, the
|
|
number of completely unmapped Namess whose Domain Prefices are
|
|
identified by this routine being subtracted from the input value.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully. Note that some
|
|
or all of the Names may remain partially or completely unmapped.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG RemainingCompletelyUnmappedCount;
|
|
ULONG NameIndex;
|
|
PLSAPR_UNICODE_STRING DomainName = &TrustInformation->Name;
|
|
BOOLEAN DomainAlreadyAdded = FALSE;
|
|
LONG DomainIndex;
|
|
|
|
//
|
|
// Scan the array of Names looking for composite ones whose domain has
|
|
// not been found.
|
|
//
|
|
|
|
for( NameIndex = 0,
|
|
RemainingCompletelyUnmappedCount = *CompletelyUnmappedCount;
|
|
(RemainingCompletelyUnmappedCount > 0) && (NameIndex < Count);
|
|
NameIndex++) {
|
|
|
|
//
|
|
// Check if this Name is completely unmapped (i.e. its domain
|
|
// has not yet been identified).
|
|
//
|
|
|
|
if (TranslatedSids->Sids[NameIndex].DomainIndex == LSA_UNKNOWN_INDEX) {
|
|
|
|
//
|
|
// Found a completely unmapped Name. If it belongs to the
|
|
// specified Domain, add the Domain to the Referenced Domain
|
|
// list if we have not already done so.
|
|
//
|
|
|
|
if (LsapRtlPrefixName(
|
|
(PUNICODE_STRING) DomainName,
|
|
(PUNICODE_STRING) &Names[NameIndex])
|
|
) {
|
|
|
|
if (!DomainAlreadyAdded) {
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation,
|
|
&DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
DomainAlreadyAdded = TRUE;
|
|
}
|
|
|
|
//
|
|
// Reference the domain from the TranslatedNames entry
|
|
//
|
|
|
|
TranslatedSids->Sids[NameIndex].DomainIndex = DomainIndex;
|
|
|
|
//
|
|
// This name is now partially translated, so reduce the
|
|
// count of completely unmapped names.
|
|
//
|
|
|
|
(*CompletelyUnmappedCount)--;
|
|
}
|
|
|
|
//
|
|
// Decrement count of completely unmapped Names scanned.
|
|
//
|
|
|
|
RemainingCompletelyUnmappedCount--;
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupDispatchWorkerThreads(
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function dispatches sufficient worker threads to handle a
|
|
Lookup operation. The worker threads can handle work items
|
|
on any Lookup's Work List, so the number of existing active
|
|
threads is taken into account. Note that the total number of
|
|
active Lookup Worker Threads may exceed the guide maximum number of
|
|
threads, in situations where an active thread terminates during
|
|
the dispatch cycle. This strategy saves having to recheck the
|
|
active thread count each time a thread is dispatched.
|
|
|
|
NOTE: This routine expects the specified pointer to a Work List to be
|
|
valid. A Work List pointer always remains valid until its
|
|
Primary thread detects completion of the Work List via this
|
|
routine and then deletes it via LsapDbLookupDeleteWorkList().
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List describing a Lookup Sid or Lookup Name
|
|
operation.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
NTSTATUS DispatchThreadStatus = STATUS_SUCCESS;
|
|
ULONG AdvisoryChildThreadCount;
|
|
ULONG DispatchThreadCount;
|
|
ULONG MaximumDispatchChildThreadCount;
|
|
ULONG ThreadNumber;
|
|
HANDLE Thread = NULL;
|
|
DWORD Ignore;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Acquire the Lookup Work Queue Lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupDispatchWorkerThreadsError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
//
|
|
// Calculate the number of Worker Threads to dispatch (if any). If
|
|
// the WorkList has an Advisory Child Thread Count of 0, we will
|
|
// not dispatch any threads, but instead will perform this Lookup
|
|
// within this thread. If the WorkList has an Advisory Child Thread
|
|
// Count > 0, we will dispatch additional threads. The number of
|
|
// additional child threads dispatched is given by the formula:
|
|
//
|
|
// ThreadsToDispatch =
|
|
// min (MaximumChildThreadCount - ActiveChildThreadCount,
|
|
// AdvisoryChildThreadCount)
|
|
//
|
|
|
|
AdvisoryChildThreadCount = WorkList->AdvisoryChildThreadCount;
|
|
|
|
if (AdvisoryChildThreadCount > 0) {
|
|
|
|
MaximumDispatchChildThreadCount =
|
|
LookupWorkQueue.MaximumChildThreadCount -
|
|
LookupWorkQueue.ActiveChildThreadCount;
|
|
|
|
if (AdvisoryChildThreadCount <= MaximumDispatchChildThreadCount) {
|
|
|
|
DispatchThreadCount = AdvisoryChildThreadCount;
|
|
|
|
} else {
|
|
|
|
DispatchThreadCount = MaximumDispatchChildThreadCount;
|
|
}
|
|
|
|
//
|
|
// Release the Lookup Work Queue Lock
|
|
//
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Signal the event that indicates that a new Work List is initiated.
|
|
//
|
|
|
|
Status = NtSetEvent( LsapDbLookupStartedEvent, NULL );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
LsapDiagPrint( DB_LOOKUP_WORK_LIST,
|
|
("LsapDbLookupDispatchWorkList... NtSetEvent failed 0x%lx\n",Status));
|
|
goto LookupDispatchWorkerThreadsError;
|
|
}
|
|
|
|
//
|
|
// Dispatch the computed number of threads.
|
|
//
|
|
|
|
for (ThreadNumber = 0; ThreadNumber < DispatchThreadCount; ThreadNumber++) {
|
|
|
|
Thread = CreateThread(
|
|
NULL,
|
|
0L,
|
|
(LPTHREAD_START_ROUTINE) LsapDbLookupWorkerThreadStart,
|
|
NULL,
|
|
0L,
|
|
&Ignore
|
|
);
|
|
|
|
if (Thread == NULL) {
|
|
|
|
Status = GetLastError();
|
|
|
|
KdPrint(("LsapDbLookupDispatchWorkerThreads: CreateThread failed 0x%lx\n"));
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
CloseHandle( Thread );
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
DispatchThreadStatus = Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do some work in the main thread too.
|
|
//
|
|
|
|
LsapDbLookupWorkerThread( TRUE);
|
|
|
|
LookupDispatchWorkerThreadsFinish:
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupDispatchWorkerThreadsError:
|
|
|
|
goto LookupDispatchWorkerThreadsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbOpenPolicyTrustedDomain(
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PLSA_HANDLE ControllerPolicyHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function opens a handle to the Lsa Policy Database on a
|
|
DC for a given Trusted Domain. The Domain may be specified either
|
|
by Sid or by Name.
|
|
|
|
Arguments:
|
|
|
|
|
|
TrustInformation - Specifies the Sid and/or Name of the Trusted Domain
|
|
whose Policy database is to be opened.
|
|
|
|
DesiredAccess - Specifies the desired access type to be granted.
|
|
|
|
ControllerPolicyHandle - Receives handle oto the LSA Policy Object
|
|
for the Lsa Policy database located on some DC for the specified
|
|
Trusted Domain.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_NO_MORE_ENTRIES - The DC list for the specified domain
|
|
is null.
|
|
|
|
Result codes from called routines.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
NTSTATUS SecondaryStatus;
|
|
LSAPR_UNICODE_STRING DCName;
|
|
PLSAPR_UNICODE_STRING DomainName = NULL;
|
|
PLSAPR_TRUST_INFORMATION OutputTrustInformation = NULL;
|
|
LSAPR_HANDLE TrustedDomainHandle = NULL;
|
|
PLSAPR_TRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
|
|
PLSAPR_SID DomainSid = NULL;
|
|
LSA_HANDLE OutputControllerPolicyHandle = NULL;
|
|
ULONG ControllerIndex, ControllerCount;
|
|
|
|
DCName.Length = DCName.MaximumLength = (USHORT) 0;
|
|
DCName.Buffer = NULL;
|
|
|
|
//
|
|
// If we're given the Domain Sid only, we need to get its name.
|
|
//
|
|
|
|
if ((TrustInformation->Name.Length == 0) ||
|
|
(TrustInformation->Name.Buffer == NULL)) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
if (TrustInformation->Sid == NULL) {
|
|
|
|
goto OpenPolicyTrustedDomainError;
|
|
}
|
|
|
|
Status = LsapDbLookupSidTrustedDomainList(
|
|
NULL,
|
|
TrustInformation->Sid,
|
|
&OutputTrustInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenPolicyTrustedDomainError;
|
|
}
|
|
|
|
DomainName = &OutputTrustInformation->Name;
|
|
|
|
} else {
|
|
|
|
DomainName = &TrustInformation->Name;
|
|
}
|
|
|
|
//
|
|
// Use netlogon services to locate a DC Name routine if possible. Even
|
|
// if the routine exists, it will return an error if netlogon.dll
|
|
// is not loaded.
|
|
//
|
|
|
|
Status = I_NetGetAnyDCName(
|
|
(PUNICODE_STRING) DomainName,
|
|
(PUNICODE_STRING) &DCName
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (DCName.Length != 0) {
|
|
|
|
Status = LsapRtlValidateControllerTrustedDomain(
|
|
&DCName,
|
|
TrustInformation,
|
|
DesiredAccess,
|
|
&OutputControllerPolicyHandle
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
goto OpenPolicyTrustedDomainFinish;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// An error occurred trying to obtain the name of a DC via net logon
|
|
// services. If the the error is simply that netlogon.dll is not loaded
|
|
// or running, fall back to the Trusted Controllers List in the LSA's
|
|
// TrustedDomain object for the Domain. Any other error means that
|
|
// there is also no point using the Trusted Controllers List either.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (!(Status == STATUS_NETLOGON_NOT_STARTED) ||
|
|
(Status == STATUS_DLL_NOT_FOUND)) {
|
|
|
|
goto OpenPolicyTrustedDomainError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We could not use netlogon.dll services to locate a DC only because
|
|
// netlogon.dll was not loaded and/or initilaized. Resort to the Trusted
|
|
// Controllers Info held in the TrustedDomain object. To access this
|
|
// object we first need to get the Domain Sid if we don't have it already.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
DomainSid = TrustInformation->Sid;
|
|
|
|
if (DomainSid == NULL) {
|
|
|
|
Status = LsapDbLookupNameTrustedDomainList(
|
|
NULL,
|
|
&TrustInformation->Name,
|
|
&OutputTrustInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenPolicyTrustedDomainError;
|
|
}
|
|
|
|
DomainSid = OutputTrustInformation->Sid;
|
|
}
|
|
|
|
//
|
|
// Open the Trusted Domain object on the local machine.
|
|
//
|
|
|
|
Status = LsarOpenTrustedDomain(
|
|
LsapPolicyHandle,
|
|
DomainSid,
|
|
TRUSTED_QUERY_CONTROLLERS,
|
|
&TrustedDomainHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
goto OpenPolicyTrustedDomainError;
|
|
}
|
|
|
|
goto OpenPolicyTrustedDomainFinish;
|
|
}
|
|
|
|
//
|
|
// Now obtain the list of Domain Controllers for the Primary Domain.
|
|
// Any error here is a problem with the local machine and should
|
|
// be reported. Note that no error is returned just because
|
|
// the trusted controllers info has never been set.
|
|
//
|
|
|
|
Status = LsarQueryInfoTrustedDomain(
|
|
TrustedDomainHandle,
|
|
TrustedControllersInformation,
|
|
(PLSAPR_TRUSTED_DOMAIN_INFO *) &TrustedControllersInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenPolicyTrustedDomainError;
|
|
}
|
|
|
|
Status = STATUS_NO_MORE_ENTRIES;
|
|
|
|
ControllerCount = TrustedControllersInfo->Entries;
|
|
|
|
if (ControllerCount == (ULONG) 0) {
|
|
|
|
goto OpenPolicyTrustedDomainError;
|
|
}
|
|
|
|
//
|
|
// Pick a valid DC from the list and open its Policy Object.
|
|
// NOTE: This algorithm is non-optimal as the selection should
|
|
// randomly try all the controllers. This doesn't matter much since
|
|
// this code is executed only as a default when CliffV's algorithm
|
|
// is not implemented.
|
|
//
|
|
|
|
for (ControllerIndex = 0; ControllerIndex < ControllerCount; ControllerIndex++) {
|
|
|
|
DCName = TrustedControllersInfo->Names[ControllerIndex];
|
|
|
|
//
|
|
// Validate the next DC on the list and if valid, return a handle,
|
|
//
|
|
|
|
Status = LsapRtlValidateControllerTrustedDomain(
|
|
&DCName,
|
|
TrustInformation,
|
|
DesiredAccess,
|
|
&OutputControllerPolicyHandle
|
|
);
|
|
|
|
//
|
|
// Intentionally ignore the error status, except for the
|
|
// final controller tried.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenPolicyTrustedDomainError;
|
|
}
|
|
|
|
OpenPolicyTrustedDomainFinish:
|
|
|
|
*ControllerPolicyHandle = OutputControllerPolicyHandle;
|
|
|
|
//
|
|
// If necessary, close the Trusted Domain object handle.
|
|
//
|
|
|
|
if (TrustedDomainHandle != NULL) {
|
|
|
|
SecondaryStatus = LsarClose( &TrustedDomainHandle );
|
|
|
|
TrustedDomainHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for the DCName buffer.
|
|
//
|
|
|
|
if (DCName.Buffer != NULL) {
|
|
|
|
MIDL_user_free( DCName.Buffer );
|
|
}
|
|
|
|
//
|
|
// If allocated, free the TrustedControllersInfo top-level structure
|
|
//
|
|
|
|
if (TrustedControllersInfo != NULL) {
|
|
|
|
MIDL_user_free(TrustedControllersInfo);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
OpenPolicyTrustedDomainError:
|
|
|
|
goto OpenPolicyTrustedDomainFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapRtlValidateControllerTrustedDomain(
|
|
IN PLSAPR_UNICODE_STRING DomainControllerName,
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PLSA_HANDLE ControllerPolicyHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function verifies that a specified computer is a DC for
|
|
a specified Domain and opens the LSA Policy Object with the
|
|
desired accesses.
|
|
|
|
Arguments:
|
|
|
|
DomainControllerName - Pointer to Unicode String computer name.
|
|
|
|
TrustInformation - Domain Trust Information. Only the Sid is
|
|
used.
|
|
|
|
DesiredAccess - Specifies the accesses desired to the target machine's
|
|
Lsa Policy Database.
|
|
|
|
PolicyHandle - Receives handle to the Lsa Policy object on the target
|
|
machine.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_OBJECT_NAME_NOT_FOUND - The specified computer is not a
|
|
Domain Controller for the specified Domain.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
NTSTATUS SecondaryStatus = STATUS_SUCCESS;
|
|
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
LSA_HANDLE OutputControllerPolicyHandle = NULL;
|
|
PLSAPR_POLICY_PRIMARY_DOM_INFO PolicyPrimaryDomainInfo = NULL;
|
|
PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;
|
|
|
|
//
|
|
// Open a handle to the Policy Object on the target DC.
|
|
//
|
|
|
|
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
|
|
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
SecurityQualityOfService.EffectiveOnly = FALSE;
|
|
|
|
//
|
|
// Set up the object attributes prior to opening the LSA.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
NULL,
|
|
0L,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// The InitializeObjectAttributes macro presently stores NULL for
|
|
// the SecurityQualityOfService field, so we must manually copy that
|
|
// structure for now.
|
|
//
|
|
|
|
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
|
|
|
|
Status = LsaOpenPolicy(
|
|
(PUNICODE_STRING) DomainControllerName,
|
|
&ObjectAttributes,
|
|
POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
|
|
&OutputControllerPolicyHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto ValidateControllerTrustedDomainError;
|
|
}
|
|
|
|
//
|
|
// We successfully opened the Policy object on the Controller's
|
|
// Lsa. Check that the target system is a DC for the Domain. We
|
|
// verify that its Primary Domain and Account Domain information
|
|
// both specify the given domain. This is a hack because there is
|
|
// no equivalent of RtlGetNtProductType for remote systems. So,
|
|
// first compare the Primary Domain Info...
|
|
//
|
|
|
|
Status = LsaQueryInformationPolicy(
|
|
OutputControllerPolicyHandle,
|
|
PolicyPrimaryDomainInformation,
|
|
(PVOID *) &PolicyPrimaryDomainInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto ValidateControllerTrustedDomainError;
|
|
}
|
|
|
|
//
|
|
// Now compare the Domain Name and Sid stored for the Controller's
|
|
// Primary Domain with the Domain Name and Sid provided.
|
|
//
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
if (!RtlEqualDomainName(
|
|
(PUNICODE_STRING) &TrustInformation->Name,
|
|
(PUNICODE_STRING) &PolicyPrimaryDomainInfo->Name
|
|
)) {
|
|
|
|
goto ValidateControllerTrustedDomainError;
|
|
}
|
|
|
|
if (!RtlEqualSid(
|
|
(PSID) TrustInformation->Sid,
|
|
(PSID) PolicyPrimaryDomainInfo->Sid
|
|
)) {
|
|
|
|
goto ValidateControllerTrustedDomainError;
|
|
}
|
|
|
|
//
|
|
// Now compare the Account Domain info.
|
|
//
|
|
|
|
Status = LsaQueryInformationPolicy(
|
|
OutputControllerPolicyHandle,
|
|
PolicyAccountDomainInformation,
|
|
(PVOID *) &PolicyAccountDomainInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto ValidateControllerTrustedDomainError;
|
|
}
|
|
|
|
//
|
|
// Now compare the Domain Name and Sid stored for the Controller's
|
|
// Account Domain with the Domain Name and Sid provided.
|
|
//
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
if (!RtlEqualDomainName(
|
|
(PUNICODE_STRING) &TrustInformation->Name,
|
|
(PUNICODE_STRING) &PolicyAccountDomainInfo->DomainName
|
|
)) {
|
|
|
|
goto ValidateControllerTrustedDomainError;
|
|
}
|
|
|
|
if (!RtlEqualSid(
|
|
(PSID) TrustInformation->Sid,
|
|
(PSID) PolicyAccountDomainInfo->DomainSid
|
|
)) {
|
|
|
|
goto ValidateControllerTrustedDomainError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
ValidateControllerTrustedDomainFinish:
|
|
|
|
//
|
|
// Free up the Policy Primary Domain Info.
|
|
//
|
|
|
|
if (PolicyPrimaryDomainInfo != NULL) {
|
|
|
|
LsaFreeMemory( (PVOID) PolicyPrimaryDomainInfo );
|
|
|
|
PolicyPrimaryDomainInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// Free up the Policy Account Domain Info.
|
|
//
|
|
|
|
if (PolicyAccountDomainInfo != NULL) {
|
|
|
|
LsaFreeMemory( (PVOID) PolicyAccountDomainInfo );
|
|
|
|
PolicyAccountDomainInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// Return Controller Policy handle or NULL.
|
|
//
|
|
|
|
*ControllerPolicyHandle = OutputControllerPolicyHandle;
|
|
|
|
return(Status);
|
|
|
|
ValidateControllerTrustedDomainError:
|
|
|
|
//
|
|
// Close the last Controller's Policy Handle.
|
|
//
|
|
|
|
if (OutputControllerPolicyHandle != NULL) {
|
|
|
|
SecondaryStatus = LsaClose( OutputControllerPolicyHandle );
|
|
OutputControllerPolicyHandle = NULL;
|
|
}
|
|
|
|
goto ValidateControllerTrustedDomainFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupAcquireWorkQueueLock(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function acquires the LSA Database Lookup Sids/Names Work Queue Lock.
|
|
This lock serializes additions or deletions of work lists to/from the
|
|
queue, and sign-in/sign-out of work items by worker threads.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = RtlEnterCriticalSection(&LookupWorkQueue.Lock);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapDbLookupReleaseWorkQueueLock(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function releases the LSA Database Lookup Sids/Names Work Queue Lock.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None. Any error occurring within this routine is an internal error.
|
|
|
|
--*/
|
|
|
|
{
|
|
RtlLeaveCriticalSection(&LookupWorkQueue.Lock);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupNamesBuildWorkList(
|
|
IN ULONG Count,
|
|
IN PLSAPR_UNICODE_STRING Names,
|
|
IN PLSAPR_UNICODE_STRING PrefixNames,
|
|
IN PLSAPR_UNICODE_STRING SuffixNames,
|
|
IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount,
|
|
OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function constructs a Work List for an LsarLookupNames call. The
|
|
Work List contains the parameters of the call, and an array of Work Items.
|
|
Each Work Item specifies either all of the Names to be looked up
|
|
in a given domain.
|
|
|
|
Qualified names (i.e. those of the form DomainName\UserName) are
|
|
sorted into different Work Items, one for each DomainName specifed.
|
|
Unqualified names (i.e. those of the form UserName) are added to
|
|
every Work Item.
|
|
|
|
Arguments:
|
|
|
|
Count - Specifies the number of names to be translated.
|
|
|
|
Names - Pointer to an array of Count Unicode String structures
|
|
specifying the names to be looked up and mapped to Sids.
|
|
The strings may be names of User, Group or Alias accounts or
|
|
domains.
|
|
|
|
PrefixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Prefix portions of the Names. Names having no
|
|
Prefix are called Isolated Names. For these, the Unicode String
|
|
structure is set to contain a zero Length.
|
|
|
|
SuffixNames - Pointer to an array of Count Unicode String structures
|
|
containing the Suffix portions of the Names.
|
|
|
|
ReferencedDomains - receives a pointer to a structure describing the
|
|
domains used for the translation. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for
|
|
each translated name, this structure will only contain one
|
|
component for each domain utilized in the translation.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
TranslatedSids - Pointer to a structure which will (or already) references an array of
|
|
records describing each translated Sid. The nth entry in this array
|
|
provides a translation for the nth element in the Names parameter.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on this
|
|
machine. Values of this field are are follows:
|
|
|
|
LsapLookupWksta - First Level Lookup performed on a workstation
|
|
normally configured for Windows-Nt. The lookup searches the
|
|
Well-Known Sids/Names, and the Built-in Domain and Account Domain
|
|
in the local SAM Database. If not all Sids or Names are
|
|
identified, performs a "handoff" of a Second level Lookup to the
|
|
LSA running on a Controller for the workstation's Primary Domain
|
|
(if any).
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Sids or Names are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
MappedCount - Pointer to location that contains a count of the Names
|
|
mapped so far. On exit, this will be updated.
|
|
|
|
MappedCount - Pointer to location containing the number of Names
|
|
in the Names array that have already been mapped. This number
|
|
will be updated to reflect additional mapping done by this
|
|
routine.
|
|
|
|
CompletelyUnmappedCount - Pointer to location containing the
|
|
count of completely unmapped Names. A Name is completely unmapped
|
|
if it is unknown and non-composite, or composite but with an
|
|
unrecognized Domain component. This count is updated on exit, the
|
|
number of completely unmapped Namess whose Domain Prefices are
|
|
identified by this routine being subtracted from the input value.
|
|
|
|
WorkList - Receives pointer to completed Work List if successfully built.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_NONE_MAPPED - All of the Names specified were composite,
|
|
but none of their Domains appear in the Trusted Domain List.
|
|
No Work List has been generated. Note that this is not a
|
|
fatal error.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS, IgnoreStatus = STATUS_SUCCESS;
|
|
PLSAP_DB_LOOKUP_WORK_LIST OutputWorkList = NULL;
|
|
ULONG NameIndex;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM WorkItemToUpdate = NULL;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM NewWorkItem = NULL;
|
|
PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
|
|
LONG DomainIndex;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM IsolatedNamesWorkItem = NULL;
|
|
BOOLEAN AcquiredTrustedDomainListReadLock = FALSE;
|
|
PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION TrustedDomainListSection = NULL;
|
|
ULONG TrustedDomainListSectionIndex;
|
|
PLSAPR_UNICODE_STRING DomainName = NULL;
|
|
PLSAPR_UNICODE_STRING TerminalName = NULL;
|
|
|
|
//
|
|
// Create an empty Work List.
|
|
//
|
|
|
|
Status = LsapDbLookupCreateWorkList(&OutputWorkList);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesBuildWorkListError;
|
|
}
|
|
|
|
//
|
|
// Initialize the Work List Header.
|
|
//
|
|
|
|
OutputWorkList->Status = STATUS_SUCCESS;
|
|
OutputWorkList->State = InactiveWorkList;
|
|
OutputWorkList->LookupType = LookupNames;
|
|
OutputWorkList->Count = Count;
|
|
OutputWorkList->LookupLevel = LookupLevel;
|
|
OutputWorkList->ReferencedDomains = ReferencedDomains;
|
|
OutputWorkList->MappedCount = MappedCount;
|
|
OutputWorkList->CompletelyUnmappedCount = CompletelyUnmappedCount;
|
|
OutputWorkList->LookupNamesParams.Names = Names;
|
|
OutputWorkList->LookupNamesParams.TranslatedSids = TranslatedSids;
|
|
|
|
//
|
|
// Construct the array of Work Items. Each Work Item will
|
|
// contain all the Names for a given domain, so we will scan
|
|
// all of the Names, sorting them into Work Items as we go.
|
|
// For each Name, follow the steps detailed below.
|
|
//
|
|
|
|
for (NameIndex = 0; NameIndex < Count; NameIndex++) {
|
|
|
|
//
|
|
// If this Name's Domain is already marked as known, skip.
|
|
//
|
|
|
|
if (TranslatedSids->Sids[NameIndex].DomainIndex != LSA_UNKNOWN_INDEX) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Name is completely unknown. See if there is already a Work Item
|
|
// for its Domain.
|
|
//
|
|
|
|
AnchorWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) OutputWorkList->AnchorWorkItem;
|
|
NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) AnchorWorkItem->Links.Flink;;
|
|
WorkItemToUpdate = NULL;
|
|
NewWorkItem = NULL;
|
|
|
|
//
|
|
// If this is a qualified name (e.g. "NtDev\ScottBi") proceed
|
|
// to search for its Domain.
|
|
//
|
|
|
|
DomainName = &PrefixNames[ NameIndex ];
|
|
TerminalName = &SuffixNames[ NameIndex ];
|
|
|
|
if (DomainName->Length != 0) {
|
|
|
|
while (NextWorkItem != AnchorWorkItem) {
|
|
|
|
if (RtlEqualDomainName(
|
|
(PUNICODE_STRING) &NextWorkItem->TrustInformation.Name,
|
|
(PUNICODE_STRING) DomainName
|
|
)) {
|
|
|
|
//
|
|
// A Work Item already exists for the Name's Trusted Domain.
|
|
// Select that Work Item for update.
|
|
//
|
|
|
|
WorkItemToUpdate = NextWorkItem;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Name's domain not found among existing Work Items. Skip to
|
|
// next Work Item.
|
|
//
|
|
|
|
NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) NextWorkItem->Links.Flink;
|
|
}
|
|
|
|
if (WorkItemToUpdate == NULL) {
|
|
|
|
//
|
|
// No Work Item exists for the Name's Domain. See if the
|
|
// Name belongs to one of the Trusted Domains. If not, skip
|
|
// to the next Name.
|
|
//
|
|
|
|
if (!AcquiredTrustedDomainListReadLock) {
|
|
|
|
Status = LsapDbAcquireReadLockTrustedDomainList( NULL );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
AcquiredTrustedDomainListReadLock = TRUE;
|
|
}
|
|
|
|
Status = LsapDbLookupNameTrustedDomainList(
|
|
NULL,
|
|
DomainName,
|
|
&TrustInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (Status == STATUS_NO_SUCH_DOMAIN) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Name belongs to a Trusted Domain for which there is
|
|
// no Work Item. Add the Domain to the Referenced Domain List
|
|
// and obtain a Domain Index.
|
|
//
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation,
|
|
&DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create a new Work Item for this domain.
|
|
//
|
|
|
|
Status = LsapDbLookupCreateWorkItem(
|
|
TrustInformation,
|
|
DomainIndex,
|
|
(ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
|
|
&NewWorkItem
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add the Work Item to the List.
|
|
//
|
|
|
|
Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
WorkItemToUpdate = NewWorkItem;
|
|
}
|
|
|
|
//
|
|
// Add the Name Index to the Work Item.
|
|
//
|
|
|
|
Status = LsapDbLookupAddIndicesToWorkItem(
|
|
WorkItemToUpdate,
|
|
(ULONG) 1,
|
|
&NameIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Store the Domain Index in the Translated Sids array entry for
|
|
// the Name.
|
|
//
|
|
|
|
OutputWorkList->LookupNamesParams.TranslatedSids->Sids[NameIndex].DomainIndex = WorkItemToUpdate->DomainIndex;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesBuildWorkListError;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is an Isolated Name. We know that it is not the name
|
|
// of any Domain on the lookup path, because all of these
|
|
// have been translated earlier. We will add the name to a
|
|
// temporary work item and later transfer all the Isolated Names
|
|
// to every Work Item on the Work List. If we don't already
|
|
// have a temporary Work Item for the Isolated Names, create one.
|
|
//
|
|
|
|
if (IsolatedNamesWorkItem == NULL) {
|
|
|
|
//
|
|
// Create a new Work Item for the Isolated Names.
|
|
// This is temporary.
|
|
//
|
|
|
|
Status = LsapDbLookupCreateWorkItem(
|
|
NULL,
|
|
DomainIndex,
|
|
(ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
|
|
&IsolatedNamesWorkItem
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark this Work Item as having Isolated Names only.
|
|
//
|
|
|
|
IsolatedNamesWorkItem->Properties = LSAP_DB_LOOKUP_WORK_ITEM_ISOL;
|
|
|
|
//
|
|
// Add the Name index to the Isolated Names Work Item
|
|
//
|
|
|
|
Status = LsapDbLookupAddIndicesToWorkItem(
|
|
IsolatedNamesWorkItem,
|
|
(ULONG) 1,
|
|
&NameIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesBuildWorkListError;
|
|
}
|
|
|
|
//
|
|
// If we have any unmapped Isolated Names, we know at this stage that they are
|
|
// not the Names of Trusted Domains. Therefore, we need to arrange
|
|
// for them to be looked up in every Trusted Domain. We need to
|
|
// traverse the list of Trusted Domains and for each Domain, either
|
|
// create a new Work Item to lookup all of the Names in that Domain,
|
|
// or add the Names to the existing Work Item generated for that
|
|
// Domain (because there are some Qualified Names which reference
|
|
// the Domain. We do this Work Item Generation in 2 stages. First,
|
|
// we will scan the Work List, adding the Isolated Names to every Work
|
|
// Item found therein. Second, we will create a Work Item for each
|
|
// of the Trusted Domains that we don't already have a Work Item.
|
|
//
|
|
|
|
//
|
|
// Stage (1) - Scan the Work List, adding the Isolated Names to
|
|
// every Work Item found therein
|
|
//
|
|
|
|
if (IsolatedNamesWorkItem != NULL) {
|
|
|
|
//
|
|
// Stage (1) - Scan the Work List, adding the Isolated Names to
|
|
// every Work Item found therein
|
|
//
|
|
|
|
NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
|
|
OutputWorkList->AnchorWorkItem->Links.Flink;
|
|
|
|
while (NextWorkItem != OutputWorkList->AnchorWorkItem) {
|
|
|
|
//
|
|
// Add the Isolated Name indices to this Work Item
|
|
//
|
|
|
|
Status = LsapDbLookupAddIndicesToWorkItem(
|
|
NextWorkItem,
|
|
IsolatedNamesWorkItem->UsedCount,
|
|
IsolatedNamesWorkItem->Indices
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
|
|
NextWorkItem->Links.Flink;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesBuildWorkListError;
|
|
}
|
|
|
|
//
|
|
// Stage (2) - Now create Work Items to look up all the Isolated Names
|
|
// in every Trusted Domain that does not presently have a Work Item.
|
|
// The Domains for all existing Work Items are already present
|
|
// on the Referenced Domains List because they all specify at least one
|
|
// Qualified Name, so we can lookup that list to determine whether a
|
|
// Work Item exists for a Domain.
|
|
//
|
|
|
|
TrustedDomainListSection = NULL;
|
|
TrustedDomainListSectionIndex = (ULONG) 0;
|
|
|
|
for (;;) {
|
|
|
|
NewWorkItem = NULL;
|
|
|
|
Status = LsapDbTraverseTrustedDomainList(
|
|
NULL,
|
|
&TrustedDomainListSection,
|
|
&TrustedDomainListSectionIndex,
|
|
&TrustInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Found Next Trusted Domain. Check if this domain is already
|
|
// present on the Referenced Domain List. If so, skip to the
|
|
// next domain, because we have a Work Item for this domain.
|
|
//
|
|
// If the Domain is not present on the Referenced Domain List,
|
|
// we need to create a Work Item for this Domain and add all
|
|
// of the unmapped Isolated Names indices to it.
|
|
//
|
|
|
|
if (LsapDbLookupListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation->Sid,
|
|
&DomainIndex
|
|
)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We don't have a Work Item for this Trusted Domain. Create
|
|
// one, and add all of the remaining Isolated Names to it.
|
|
// Mark the Domain Index as unknown. This is picked up
|
|
// later when the Work Item is processed. If any Names
|
|
// were translated, the Domain to which the Work Item
|
|
// relates will be added to the Referenced Domain List.
|
|
//
|
|
|
|
Status = LsapDbLookupCreateWorkItem(
|
|
TrustInformation,
|
|
LSA_UNKNOWN_INDEX,
|
|
(ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
|
|
&NewWorkItem
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Mark this Work Item as having Isolated Names only.
|
|
//
|
|
|
|
NewWorkItem->Properties = LSAP_DB_LOOKUP_WORK_ITEM_ISOL;
|
|
|
|
//
|
|
// Add the Isolated Name indices to this Work Item
|
|
//
|
|
|
|
Status = LsapDbLookupAddIndicesToWorkItem(
|
|
NewWorkItem,
|
|
IsolatedNamesWorkItem->UsedCount,
|
|
IsolatedNamesWorkItem->Indices
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add the Work Item to the List.
|
|
//
|
|
|
|
Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesBuildWorkListError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the Work List has no Work Items, this means that all of the
|
|
// Names were composite, but none of the Domain Names specified
|
|
// could be found on the Trusted Domain List. In this case,
|
|
// we discard the Work List.
|
|
//
|
|
|
|
Status = STATUS_NONE_MAPPED;
|
|
|
|
if (OutputWorkList->WorkItemCount == 0) {
|
|
|
|
goto LookupNamesBuildWorkListError;
|
|
}
|
|
|
|
//
|
|
// Compute the Advisory Thread Count for this lookup.
|
|
//
|
|
|
|
Status = LsapDbLookupComputeAdvisoryChildThreadCount( OutputWorkList );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesBuildWorkListError;
|
|
}
|
|
|
|
Status = LsapDbLookupInsertWorkList(OutputWorkList);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesBuildWorkListError;
|
|
}
|
|
|
|
//
|
|
// Update the Mapped Counts
|
|
//
|
|
|
|
LsapDbUpdateMappedCountsWorkList( OutputWorkList );
|
|
|
|
LookupNamesBuildWorkListFinish:
|
|
|
|
//
|
|
// If necessary, release the Trusted Domain List Read Lock.
|
|
//
|
|
|
|
if (AcquiredTrustedDomainListReadLock) {
|
|
|
|
LsapDbReleaseReadLockTrustedDomainList( NULL );
|
|
AcquiredTrustedDomainListReadLock = FALSE;
|
|
}
|
|
|
|
//
|
|
// If we have an Isolated Names Work Item, free it.
|
|
//
|
|
|
|
if (IsolatedNamesWorkItem != NULL) {
|
|
|
|
MIDL_user_free( IsolatedNamesWorkItem->Indices);
|
|
IsolatedNamesWorkItem->Indices = NULL;
|
|
MIDL_user_free( IsolatedNamesWorkItem );
|
|
IsolatedNamesWorkItem = NULL;
|
|
}
|
|
|
|
*WorkList = OutputWorkList;
|
|
return(Status);
|
|
|
|
LookupNamesBuildWorkListError:
|
|
|
|
//
|
|
// Discard the Work List.
|
|
//
|
|
|
|
if (OutputWorkList != NULL) {
|
|
|
|
IgnoreStatus = LsapDbLookupDeleteWorkList(OutputWorkList);
|
|
OutputWorkList = NULL;
|
|
}
|
|
|
|
goto LookupNamesBuildWorkListFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupSidsBuildWorkList(
|
|
IN ULONG Count,
|
|
IN PLSAPR_SID *Sids,
|
|
IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN PLSAPR_TRANSLATED_NAMES TranslatedNames,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount,
|
|
OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function constructs a Work List for an LsarLookupSids call. The
|
|
Work List contains the parameters of the call, and an array of Work Items.
|
|
Each Work Item specifies all of the Sids belonging to a given Domain
|
|
and is the minimal unit of work that a worker thread will undertake.
|
|
|
|
Count - Number of Sids in the Sids array, Note that some of these
|
|
may already have been mapped elsewhere, as specified by the
|
|
MappedCount parameter.
|
|
|
|
Sids - Pointer to array of pointers to Sids to be translated.
|
|
Zero or all of the Sids may already have been translated
|
|
elsewhere. If any of the Sids have been translated, the
|
|
|
|
Names parameter will point to a location containing a non-NULL
|
|
array of Name translation structures corresponding to the
|
|
Sids. If the nth Sid has been translated, the nth name
|
|
translation structure will contain either a non-NULL name
|
|
or a non-negative offset into the Referenced Domain List. If
|
|
the nth Sid has not yet been translated, the nth name
|
|
translation structure will contain a zero-length name string
|
|
and a negative value for the Referenced Domain List index.
|
|
|
|
TrustInformation - Pointer to Trust Information specifying a Domain Sid
|
|
and Name.
|
|
|
|
ReferencedDomains - Pointer to a Referenced Domain List structure.
|
|
The structure references an array of zero or more Trust Information
|
|
entries, one per referenced domain. This array will be appended to
|
|
or reallocated if necessary.
|
|
|
|
TranslatedNames - Pointer to structure that optionally references a list
|
|
of name translations for some of the Sids in the Sids array.
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on this
|
|
machine. Values of this field are are follows:
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Sids or Names are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
NOTE: LsapLookupWksta is not valid for this parameter.
|
|
|
|
MappedCount - Pointer to location containing the number of Sids
|
|
in the Sids array that have already been mapped. This number
|
|
will be updated to reflect additional mapping done by this
|
|
routine.
|
|
|
|
CompletelyUnmappedCount - Pointer to location containing the
|
|
count of completely unmapped Sids. A Sid is completely unmapped
|
|
if it is unknown and also its Domain Prefix Sid is not recognized.
|
|
This count is updated on exit, the number of completely unmapped
|
|
Sids whose Domain Prefices are identified by this routine being
|
|
subtracted from the input value.
|
|
|
|
WorkList - Receives pointer to completed Work List if successfully built.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
STATUS_NONE_MAPPED - None of the Sids specified belong to any of
|
|
the Trusted Domains. No Work List has been generated. Note
|
|
that this is not a fatal error.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
NTSTATUS IgnoreStatus = STATUS_SUCCESS;
|
|
PLSAP_DB_LOOKUP_WORK_LIST OutputWorkList = NULL;
|
|
ULONG SidIndex;
|
|
PSID DomainSid = NULL;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM WorkItemToUpdate = NULL;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM NewWorkItem = NULL;
|
|
PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
|
|
LONG DomainIndex;
|
|
PSID Sid = NULL;
|
|
BOOLEAN AcquiredReadLockTrustedDomainList = FALSE;
|
|
|
|
//
|
|
// Create an empty Work List
|
|
//
|
|
|
|
Status = LsapDbLookupCreateWorkList(&OutputWorkList);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsBuildWorkListError;
|
|
}
|
|
|
|
//
|
|
// Initialize the rest of the Work List Header fields. Some fields
|
|
// were initialized upon creation to fixed values. The ones set here
|
|
// depend on parameter values passed into this routine.
|
|
//
|
|
|
|
OutputWorkList->LookupType = LookupSids;
|
|
OutputWorkList->Count = Count;
|
|
OutputWorkList->LookupLevel = LookupLevel;
|
|
OutputWorkList->ReferencedDomains = ReferencedDomains;
|
|
OutputWorkList->MappedCount = MappedCount;
|
|
OutputWorkList->CompletelyUnmappedCount = CompletelyUnmappedCount;
|
|
OutputWorkList->LookupSidsParams.Sids = Sids;
|
|
OutputWorkList->LookupSidsParams.TranslatedNames = TranslatedNames;
|
|
|
|
//
|
|
// Construct the array of Work Items. Each Work Item will
|
|
// contain all the Sids for a given domain, so we will scan
|
|
// all of the Sids, sorting them into Work Items as we go.
|
|
// For each Sid, follow the steps detailed below.
|
|
//
|
|
|
|
for (SidIndex = 0; SidIndex < Count; SidIndex++) {
|
|
|
|
//
|
|
// If this Sid's Domain is already marked as known, skip.
|
|
//
|
|
|
|
if (TranslatedNames->Names[SidIndex].DomainIndex != LSA_UNKNOWN_INDEX) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Sid is completely unknown. Extract its Domain Sid and see if
|
|
// there is already a Work Item for its Domain.
|
|
//
|
|
|
|
Sid = Sids[SidIndex];
|
|
|
|
Status = LsapRtlExtractDomainSid( Sid, &DomainSid );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) OutputWorkList->AnchorWorkItem->Links.Flink;
|
|
AnchorWorkItem = OutputWorkList->AnchorWorkItem;
|
|
WorkItemToUpdate = NULL;
|
|
NewWorkItem = NULL;
|
|
|
|
while (NextWorkItem != AnchorWorkItem) {
|
|
|
|
if (RtlEqualSid((PSID) NextWorkItem->TrustInformation.Sid,DomainSid)) {
|
|
|
|
//
|
|
// A Work Item already exists for the Sid's Trusted Domain.
|
|
// Select that Work Item for update.
|
|
//
|
|
|
|
MIDL_user_free(DomainSid);
|
|
WorkItemToUpdate = NextWorkItem;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Sid's domain not found among existing Work Items. Skip to
|
|
// next Work Item.
|
|
//
|
|
|
|
NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) NextWorkItem->Links.Flink;
|
|
}
|
|
|
|
if (WorkItemToUpdate == NULL) {
|
|
|
|
//
|
|
// No Work Item exists for the Sid's Domain. See if the
|
|
// Sid belongs to one of the Trusted Domains. If not, skip
|
|
// to the next Sid.
|
|
//
|
|
|
|
if (!AcquiredReadLockTrustedDomainList) {
|
|
|
|
Status = LsapDbAcquireReadLockTrustedDomainList( NULL );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
AcquiredReadLockTrustedDomainList = TRUE;
|
|
}
|
|
|
|
Status = LsapDbLookupSidTrustedDomainList(
|
|
NULL,
|
|
DomainSid,
|
|
&TrustInformation
|
|
);
|
|
|
|
MIDL_user_free(DomainSid);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
if (Status == STATUS_NO_SUCH_DOMAIN) {
|
|
|
|
//
|
|
// Try looking up the sid itself as a domain
|
|
//
|
|
|
|
Status = LsapDbLookupSidTrustedDomainList(
|
|
NULL,
|
|
Sid,
|
|
&TrustInformation
|
|
);
|
|
|
|
|
|
if (Status == STATUS_NO_SUCH_DOMAIN) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sid belongs to a Trusted Domain for which there is
|
|
// no Work Item. Add the Domain to the Referenced Domain List
|
|
// and obtain a Domain Index.
|
|
//
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation,
|
|
&DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create a new Work Item for this domain.
|
|
//
|
|
|
|
Status = LsapDbLookupCreateWorkItem(
|
|
TrustInformation,
|
|
DomainIndex,
|
|
(ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
|
|
&NewWorkItem
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add the Work Item to the List.
|
|
//
|
|
|
|
Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
WorkItemToUpdate = NewWorkItem;
|
|
}
|
|
|
|
//
|
|
// Add the Sid Index to the Work Item.
|
|
//
|
|
|
|
Status = LsapDbLookupAddIndicesToWorkItem(
|
|
WorkItemToUpdate,
|
|
(ULONG) 1,
|
|
&SidIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Store the Domain Index in the Translated Names array entry for
|
|
// the Sid.
|
|
//
|
|
|
|
OutputWorkList->LookupSidsParams.TranslatedNames->Names[SidIndex].DomainIndex = WorkItemToUpdate->DomainIndex;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsBuildWorkListError;
|
|
}
|
|
|
|
//
|
|
// If the Work List has no Work Items, this means that none of the
|
|
// Sids belong to any of the Trusted Domains. In this case,
|
|
// we discard the Work List.
|
|
//
|
|
|
|
Status = STATUS_NONE_MAPPED;
|
|
|
|
if (OutputWorkList->WorkItemCount == 0) {
|
|
|
|
goto LookupSidsBuildWorkListError;
|
|
}
|
|
|
|
//
|
|
// Compute the Advisory Thread Count for this lookup.
|
|
//
|
|
|
|
Status = LsapDbLookupComputeAdvisoryChildThreadCount( OutputWorkList );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsBuildWorkListError;
|
|
}
|
|
|
|
//
|
|
// Insert the Work List at the end of the Work Queue.
|
|
//
|
|
|
|
Status = LsapDbLookupInsertWorkList(OutputWorkList);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsBuildWorkListError;
|
|
}
|
|
|
|
//
|
|
// Update the Mapped Counts
|
|
//
|
|
|
|
LsapDbUpdateMappedCountsWorkList( OutputWorkList );
|
|
|
|
*WorkList = OutputWorkList;
|
|
|
|
LookupSidsBuildWorkListFinish:
|
|
|
|
//
|
|
// If necessary, release the Trusted Domain List Read Lock.
|
|
//
|
|
|
|
if (AcquiredReadLockTrustedDomainList) {
|
|
|
|
LsapDbReleaseReadLockTrustedDomainList( NULL );
|
|
AcquiredReadLockTrustedDomainList = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupSidsBuildWorkListError:
|
|
|
|
if ( OutputWorkList != NULL ) {
|
|
|
|
IgnoreStatus = LsapDbLookupDeleteWorkList( OutputWorkList );
|
|
OutputWorkList = NULL;
|
|
}
|
|
|
|
*WorkList = NULL;
|
|
goto LookupSidsBuildWorkListFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupCreateWorkList(
|
|
OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a Lookup Operation Work List and
|
|
initializes fixed default fields.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Receives Pointer to an empty Work List structure.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Allocate memory for the Work List header.
|
|
//
|
|
|
|
*WorkList = LsapAllocateLsaHeap( sizeof(LSAP_DB_LOOKUP_WORK_LIST) );
|
|
|
|
//
|
|
// Initialize the fixed fields in the Work List.
|
|
//
|
|
|
|
Status = LsapDbLookupInitializeWorkList(*WorkList);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupInsertWorkList(
|
|
IN PLSAP_DB_LOOKUP_WORK_LIST WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function inserts a Lookup Operation Work List in the Work Queue.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List structure describing a Lookup Sids
|
|
or Lookup Names operation.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Acquire the Lookup Work Queue Lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupInsertWorkListError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
//
|
|
// Mark the Work List as Active.
|
|
//
|
|
|
|
WorkList->State = ActiveWorkList;
|
|
|
|
//
|
|
// Link the Work List onto the end of the Work Queue.
|
|
//
|
|
|
|
WorkList->WorkLists.Flink =
|
|
(PLIST_ENTRY) LookupWorkQueue.AnchorWorkList;
|
|
WorkList->WorkLists.Blink =
|
|
(PLIST_ENTRY) LookupWorkQueue.AnchorWorkList->WorkLists.Blink;
|
|
|
|
WorkList->WorkLists.Flink->Blink = (PLIST_ENTRY) WorkList;
|
|
WorkList->WorkLists.Blink->Flink = (PLIST_ENTRY) WorkList;
|
|
|
|
//
|
|
// Update the Currently Assignable Work List and Work Item pointers
|
|
// if there is none.
|
|
//
|
|
|
|
if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {
|
|
|
|
LookupWorkQueue.CurrentAssignableWorkList = WorkList;
|
|
LookupWorkQueue.CurrentAssignableWorkItem =
|
|
(PLSAP_DB_LOOKUP_WORK_ITEM) WorkList->AnchorWorkItem->Links.Flink;
|
|
}
|
|
|
|
|
|
//
|
|
// Diagnostic message indicating work list has been inserted
|
|
//
|
|
|
|
LsapDiagPrint( DB_LOOKUP_WORK_LIST,
|
|
("LSA DB: Inserting WorkList: 0x%lx ( Item Count: %ld)\n", WorkList, WorkList->WorkItemCount) );
|
|
|
|
|
|
LookupInsertWorkListFinish:
|
|
|
|
//
|
|
// If necessary, release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupInsertWorkListError:
|
|
|
|
goto LookupInsertWorkListFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupDeleteWorkList(
|
|
IN PLSAP_DB_LOOKUP_WORK_LIST WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function Deletes a Lookup Operation Work List from the Work Queue
|
|
and frees the Work List structure.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List structure describing a Lookup Sids
|
|
or Lookup Names operation.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM ThisWorkItem = NULL;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Acquire the Lookup Work Queue Lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupDeleteWorkListError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
//
|
|
// An internal error exists if we are trying to delete an active Work List.
|
|
// Only inactive or completed Work Lists can be removed.
|
|
//
|
|
|
|
ASSERT(WorkList->State != ActiveWorkList);
|
|
|
|
//
|
|
// If the Work List is on the Work Queue, remove it.
|
|
//
|
|
|
|
if ((WorkList->WorkLists.Blink != NULL) &&
|
|
(WorkList->WorkLists.Flink != NULL)) {
|
|
|
|
WorkList->WorkLists.Blink->Flink = WorkList->WorkLists.Flink;
|
|
WorkList->WorkLists.Flink->Blink = WorkList->WorkLists.Blink;
|
|
}
|
|
|
|
//
|
|
// Release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Free up memory allocated for the Work Items on the List.
|
|
//
|
|
|
|
ThisWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) WorkList->AnchorWorkItem->Links.Blink;
|
|
|
|
while (ThisWorkItem != WorkList->AnchorWorkItem) {
|
|
|
|
NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) ThisWorkItem->Links.Blink;
|
|
|
|
if (ThisWorkItem->Indices != NULL) {
|
|
|
|
MIDL_user_free( ThisWorkItem->Indices );
|
|
}
|
|
|
|
MIDL_user_free( ThisWorkItem->TrustInformation.Sid );
|
|
|
|
MIDL_user_free( ThisWorkItem->TrustInformation.Name.Buffer );
|
|
|
|
MIDL_user_free( ThisWorkItem );
|
|
|
|
ThisWorkItem = NextWorkItem;
|
|
}
|
|
|
|
//
|
|
// Free up memory allocated for the Work List structure itself.
|
|
//
|
|
|
|
MIDL_user_free( WorkList );
|
|
|
|
LookupDeleteWorkListFinish:
|
|
|
|
//
|
|
// If necessary, release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupDeleteWorkListError:
|
|
|
|
goto LookupDeleteWorkListFinish;
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapDbUpdateMappedCountsWorkList(
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Decsription:
|
|
|
|
This function updates the counts of completely Mapped and completely
|
|
Unmapped Sids or Names in a Work List. A Sid or Name is completely
|
|
mapped if its Use has been identified, partially mapped if its
|
|
Domain is known but its terminal name of relative id is not yet
|
|
known, completely unmapped if its domain is not yet known.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to Work List to be updated.
|
|
|
|
Return Values:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
ULONG OutputMappedCount = (ULONG) 0;
|
|
ULONG OutputCompletelyUnmappedCount = WorkList->Count;
|
|
ULONG Index;
|
|
|
|
//
|
|
// Acquire the Lookup Work Queue Lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto UpdateMappedCountsWorkListError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
if (WorkList->LookupType == LookupSids) {
|
|
|
|
for ( Index = (ULONG) 0; Index < WorkList->Count; Index++ ) {
|
|
|
|
if (WorkList->LookupSidsParams.TranslatedNames->Names[ Index].Use
|
|
!= SidTypeUnknown) {
|
|
|
|
OutputMappedCount++;
|
|
OutputCompletelyUnmappedCount--;
|
|
|
|
} else if (WorkList->LookupSidsParams.TranslatedNames->Names[ Index].DomainIndex
|
|
!= LSA_UNKNOWN_INDEX) {
|
|
|
|
OutputCompletelyUnmappedCount--;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
for ( Index = (ULONG) 0; Index < WorkList->Count; Index++ ) {
|
|
|
|
if (WorkList->LookupNamesParams.TranslatedSids->Sids[ Index].Use
|
|
!= SidTypeUnknown) {
|
|
|
|
OutputMappedCount++;
|
|
OutputCompletelyUnmappedCount--;
|
|
|
|
} else if (WorkList->LookupNamesParams.TranslatedSids->Sids[ Index].DomainIndex
|
|
!= LSA_UNKNOWN_INDEX) {
|
|
|
|
OutputCompletelyUnmappedCount--;
|
|
}
|
|
}
|
|
}
|
|
|
|
*WorkList->MappedCount = OutputMappedCount;
|
|
*WorkList->CompletelyUnmappedCount = OutputCompletelyUnmappedCount;
|
|
|
|
UpdateMappedCountsWorkListFinish:
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return;
|
|
|
|
UpdateMappedCountsWorkListError:
|
|
|
|
goto UpdateMappedCountsWorkListFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupSignalCompletionWorkList(
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function signals the completion or termination of work on
|
|
a Work List.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List structure describing a Lookup Sids
|
|
or Lookup Names operation.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Verify that all work on the Work List is either complete or
|
|
// the Work List has been terminated.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSignalCompletionWorkListError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
if (NT_SUCCESS(WorkList->Status)) {
|
|
|
|
Status = STATUS_INTERNAL_DB_ERROR;
|
|
|
|
if (WorkList->CompletedWorkItemCount != WorkList->WorkItemCount) {
|
|
|
|
goto LookupSignalCompletionWorkListError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Signal the event that indicates that a Work List has been processed.
|
|
//
|
|
|
|
Status = NtSetEvent( LsapDbLookupCompleteEvent, NULL );
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
LsapDiagPrint( DB_LOOKUP_WORK_LIST,
|
|
("LSA DB: LsapDbLookupSignalCompletion.. NtSetEvent failed 0x%lx\n",Status));
|
|
goto LookupSignalCompletionWorkListError;
|
|
}
|
|
|
|
LookupSignalCompletionWorkListFinish:
|
|
|
|
|
|
LsapDiagPrint( DB_LOOKUP_WORK_LIST,
|
|
("LSA DB: Lookup completion event signalled. (Status: 0x%lx)\n"
|
|
" WorkList: 0x%lx\n", Status, WorkList) );
|
|
|
|
//
|
|
// If necessary, release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupSignalCompletionWorkListError:
|
|
|
|
goto LookupSignalCompletionWorkListFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupAwaitCompletionWorkList(
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function awaits the completion or termination of work on a
|
|
specified Work List.
|
|
|
|
NOTE: This routine expects the specified pointer to a Work List to be
|
|
valid. A Work List pointer always remains valid until its
|
|
Primary thread detects completion of the Work List via this
|
|
routine and then deletes it via LsapDbLookupDeleteWorkList().
|
|
For this reason, the Lookup Work Queue lock does not have to
|
|
be held while this routine executes.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List structure describing a Lookup Sids
|
|
or Lookup Names operation.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
LSAP_DB_LOOKUP_WORK_LIST_STATE WorkListState;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Loop, waiting for completion events to occur. When one does,
|
|
// check the status of the specified Work List.
|
|
//
|
|
|
|
for (;;) {
|
|
|
|
//
|
|
// Check for completed Work List. Since someone else may be
|
|
// setting the state, we need to read it while holding the lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
WorkListState = WorkList->State;
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
|
|
if (WorkListState == CompletedWorkList) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Wait for Work List completed event to be signalled.
|
|
//
|
|
|
|
LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("Lsa Db: Waiting on worklist completion event\n") );
|
|
Status = NtWaitForSingleObject( LsapDbLookupCompleteEvent, TRUE, NULL);
|
|
LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LsapDb: Wait on worklist completion event Done\n Status: 0x%lx\n", Status) );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupAwaitCompletionWorkListError;
|
|
}
|
|
|
|
LookupAwaitCompletionWorkListFinish:
|
|
|
|
//
|
|
// If necessary, release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupAwaitCompletionWorkListError:
|
|
|
|
|
|
LsapDiagPrint( DB_LOOKUP_WORK_LIST,
|
|
("Lsa Db: LookupAwaitWorklist error. (Status: 0x%lx)\n", Status) );
|
|
|
|
goto LookupAwaitCompletionWorkListFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbAddWorkItemToWorkList(
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
|
|
IN PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Decsription:
|
|
|
|
This function adds a Work Item to a Work List. The specified
|
|
Work Item must exist in non-volatile memory (e.g a heap block).
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List structure describing a Lookup Sids
|
|
or Lookup Names operation.
|
|
|
|
WorkItem - Pointer to a Work Item structure describing a list of
|
|
Sids or Names and a domain in which they are to be looked up.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Acquire the Lookup Work Queue Lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupAddWorkItemToWorkListError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
//
|
|
// Mark the Work Item as assignable.
|
|
//
|
|
|
|
WorkItem->State = AssignableWorkItem;
|
|
|
|
//
|
|
// Link the Work Item onto the end of the Work List and increment the
|
|
// Work Item Count.
|
|
//
|
|
|
|
WorkItem->Links.Flink = (PLIST_ENTRY) WorkList->AnchorWorkItem;
|
|
WorkItem->Links.Blink = (PLIST_ENTRY) WorkList->AnchorWorkItem->Links.Blink;
|
|
WorkItem->Links.Flink->Blink = (PLIST_ENTRY) WorkItem;
|
|
WorkItem->Links.Blink->Flink = (PLIST_ENTRY) WorkItem;
|
|
|
|
WorkList->WorkItemCount++;
|
|
|
|
LookupAddWorkItemToWorkListFinish:
|
|
|
|
//
|
|
// If necessary, release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupAddWorkItemToWorkListError:
|
|
|
|
goto LookupAddWorkItemToWorkListFinish;
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapDbLookupWorkerThreadStart(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initiates a child Worker Thread for a Lookup operation.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Start the thread's work processing loop, specifying that this
|
|
// thread is a child thread rather than the main thread.
|
|
//
|
|
|
|
LsapDbLookupWorkerThread( FALSE );
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapDbLookupWorkerThread(
|
|
IN BOOLEAN PrimaryThread
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is executed by each worker thread for a Lookup operation.
|
|
Each worker thread loops, requesting work items from the Lookup
|
|
Work Queue. Work Items assigned may belong to any current lookup.
|
|
|
|
Arguments:
|
|
|
|
PrimaryThread - TRUE if thread is the main thread of the Lookup
|
|
operation, FALSE if the thread is a child thread created by
|
|
the Lookup operation. The main thread of the Lookup operation
|
|
also processes work items, but is also responsible for collating
|
|
the results of the Lookup operation. It is not counted in the
|
|
active thread count and is not returnable to the thread pool.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM WorkItem = NULL;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// If this thread is a child worker thread, increment count of active
|
|
// child threads.
|
|
//
|
|
|
|
if (!PrimaryThread) {
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupWorkerThreadError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
LookupWorkQueue.ActiveChildThreadCount++;
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
//
|
|
// Loop while there is work to do.
|
|
//
|
|
|
|
for (;;) {
|
|
|
|
//
|
|
// Obtain work packet
|
|
//
|
|
|
|
Status = LsapDbLookupObtainWorkItem(&WorkList, &WorkItem);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = LsapDbLookupProcessWorkItem(WorkList, WorkItem);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// An error has occurred. Stop this lookup.
|
|
//
|
|
|
|
Status = LsapDbLookupStopProcessingWorkList(WorkList, Status);
|
|
|
|
//
|
|
// NOTE: Intentionally ignore the status.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If an error occurred other than there being no more work to do,
|
|
// quit.
|
|
//
|
|
|
|
if (Status != STATUS_NO_MORE_ENTRIES) {
|
|
|
|
break;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// There is no more work to do. If this thread is a child worker
|
|
// thread, either return thread to pool and wait for more work, or
|
|
// terminate if enough threads have already been retained. If this
|
|
// thread is the main thread of a Lookup operation, just return
|
|
// in order to collate results.
|
|
//
|
|
|
|
if (!PrimaryThread) {
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
if (LookupWorkQueue.ActiveChildThreadCount <= LookupWorkQueue.MaximumRetainedChildThreadCount) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Wait forever for more work.
|
|
//
|
|
|
|
Status = NtWaitForSingleObject( LsapDbLookupStartedEvent, TRUE, NULL);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// An error occurred in the wait routine. Exit the thread.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
}
|
|
|
|
//
|
|
// We already have enough active threads or an error has occurred.
|
|
// Mark this one inactive and terminate it.
|
|
//
|
|
|
|
LookupWorkQueue.ActiveChildThreadCount--;
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Terminate the thread.
|
|
//
|
|
|
|
ExitThread((DWORD) Status);
|
|
}
|
|
|
|
//
|
|
// We're the Primary Thread of some Lookup operation and there is
|
|
// no more work to do. Break out so we can return to caller.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
LookupWorkerThreadFinish:
|
|
|
|
//
|
|
// If necessary, release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return;
|
|
|
|
LookupWorkerThreadError:
|
|
|
|
goto LookupWorkerThreadFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupObtainWorkItem(
|
|
OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList,
|
|
OUT PLSAP_DB_LOOKUP_WORK_ITEM *WorkItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by a worker thread to obtain a Work Item. This
|
|
Work Item may belong to any current lookup operation.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Receives a pointer to a Work List structure describing a
|
|
Lookup Sids or Lookup Names operation.
|
|
|
|
WorkItem - Receives a pointer to a Work Item structure describing a
|
|
list of Sids or Names and a domain in which they are to be looked up.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_NO_MORE_ENTRIES - No more work items available.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
*WorkList = NULL;
|
|
*WorkItem = NULL;
|
|
|
|
//
|
|
// Acquire the Lookup Work Queue Lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupObtainWorkItemError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
//
|
|
// Return an error if there are no more Work Items.
|
|
//
|
|
|
|
Status = STATUS_NO_MORE_ENTRIES;
|
|
|
|
if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {
|
|
|
|
goto LookupObtainWorkItemError;
|
|
}
|
|
|
|
//
|
|
// Verify that the Current Assignable Work List does not have
|
|
// a termination error. This should never happen, because the
|
|
// pointers should be updated if the Lookup corresponding to the Current
|
|
// Assignable Work List is terminated.
|
|
//
|
|
|
|
ASSERT(NT_SUCCESS(LookupWorkQueue.CurrentAssignableWorkList->Status));
|
|
|
|
//
|
|
// There are work items available. Check the next one out.
|
|
//
|
|
|
|
ASSERT(LookupWorkQueue.CurrentAssignableWorkItem->State == AssignableWorkItem);
|
|
LookupWorkQueue.CurrentAssignableWorkItem->State = AssignedWorkItem;
|
|
*WorkList = LookupWorkQueue.CurrentAssignableWorkList;
|
|
*WorkItem = LookupWorkQueue.CurrentAssignableWorkItem;
|
|
|
|
//
|
|
// Update pointers to next item (if any) in the current Work List
|
|
// where work is being given out.
|
|
//
|
|
|
|
Status = LsapDbLookupUpdateAssignableWorkItem(FALSE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupObtainWorkItemError;
|
|
}
|
|
|
|
LookupObtainWorkItemFinish:
|
|
|
|
//
|
|
// If we acquired the Lookup Work Queue Lock, release it.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupObtainWorkItemError:
|
|
|
|
goto LookupObtainWorkItemFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupProcessWorkItem(
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function processes a Work Item for a Lookup operation. The Work
|
|
Item specifies a number of Sids or Names to be looked up in a given
|
|
domain.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List structure describing a
|
|
Lookup Sids or Lookup Names operation.
|
|
|
|
WorkItem - Pointer to a Work Item structure describing a
|
|
list of Sids or Names and a domain in which they are to be looked up.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
NTSTATUS SecondaryStatus;
|
|
ULONG NextLevelCount;
|
|
ULONG NextLevelMappedCount;
|
|
LSA_HANDLE ControllerPolicyHandle = NULL;
|
|
ULONG NextLevelIndex;
|
|
PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
|
|
PSID *NextLevelSids = NULL;
|
|
PUNICODE_STRING NextLevelNames = NULL;
|
|
PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
|
|
PLSA_TRANSLATED_SID NextLevelTranslatedSids = NULL;
|
|
PLSA_TRANSLATED_NAME NextLevelTranslatedNames = NULL;
|
|
ULONG Index;
|
|
|
|
LsapDiagPrint( DB_LOOKUP_WORK_LIST,
|
|
("LSA DB: Processing work item. (0x%lx, 0x%lx)\n", WorkList, WorkItem));
|
|
|
|
//
|
|
// Open a Domain Controller for this Trusted Domain.
|
|
//
|
|
|
|
Status = LsapDbOpenPolicyTrustedDomain(
|
|
&WorkItem->TrustInformation,
|
|
POLICY_LOOKUP_NAMES,
|
|
&ControllerPolicyHandle
|
|
);
|
|
|
|
//
|
|
// If we are unable to connect to the Trusted Domain via any DC,
|
|
// suppress the error so that the Lookup can continue to try and
|
|
// translate other Sids/Names.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto LookupProcessWorkItemError;
|
|
}
|
|
|
|
//
|
|
// Branch according to lookup type.
|
|
//
|
|
|
|
NextLevelCount = WorkItem->UsedCount;
|
|
|
|
if (WorkList->LookupType == LookupSids) {
|
|
|
|
//
|
|
// Allocate an array for the Sids to be looked up at a Domain
|
|
// Controller for the specified Trusted Domain.
|
|
//
|
|
|
|
NextLevelSids = MIDL_user_allocate( sizeof(PSID) * NextLevelCount );
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (NextLevelSids == NULL) {
|
|
|
|
goto LookupProcessWorkItemError;
|
|
}
|
|
|
|
//
|
|
// Copy in the Sids to be looked up from the Work List. The Work
|
|
// Item contains their indices relative to the Sid array in the
|
|
// Work List.
|
|
//
|
|
|
|
for (NextLevelIndex = 0;
|
|
NextLevelIndex < NextLevelCount;
|
|
NextLevelIndex++) {
|
|
|
|
Index = WorkItem->Indices[ NextLevelIndex ];
|
|
NextLevelSids[NextLevelIndex] = WorkList->LookupSidsParams.Sids[Index];
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
NextLevelMappedCount = (ULONG) 0;
|
|
|
|
//
|
|
// Lookup the Sids at the DC.
|
|
//
|
|
|
|
Status = LsaICLookupSids(
|
|
ControllerPolicyHandle,
|
|
NextLevelCount,
|
|
NextLevelSids,
|
|
(PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
|
|
(PLSA_TRANSLATED_NAME *) &NextLevelTranslatedNames,
|
|
WorkList->LookupLevel,
|
|
&NextLevelMappedCount
|
|
);
|
|
|
|
LsapDiagPrint( DB_LOOKUP_WORK_LIST,
|
|
("LSA DB: Sid Lookup.\n"
|
|
" Item: (0x%lx, 0x%lx)\n"
|
|
" Count: 0x%lx\n", WorkList, WorkItem, NextLevelCount));
|
|
|
|
//
|
|
// If the callout to LsaLookupSids() was unsuccessful, disregard
|
|
// the error and set the domain name for any Sids having this
|
|
// domain Sid as prefix sid.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto LookupProcessWorkItemFinish;
|
|
}
|
|
|
|
//
|
|
// The callout to LsaICLookupSids() was successful. Update the
|
|
// TranslatedNames information in the Work List as appropriate
|
|
// using the TranslatedNames information returned from the callout.
|
|
//
|
|
|
|
Status = LsapDbLookupSidsUpdateTranslatedNames(
|
|
WorkList,
|
|
WorkItem,
|
|
NextLevelTranslatedNames
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupProcessWorkItemError;
|
|
}
|
|
|
|
} else if (WorkList->LookupType == LookupNames) {
|
|
|
|
//
|
|
// Allocate an array of UNICODE_STRING structures for the
|
|
// names to be looked up at the Domain Controller.
|
|
//
|
|
|
|
NextLevelNames = MIDL_user_allocate(sizeof(UNICODE_STRING) * NextLevelCount);
|
|
|
|
if (NextLevelNames == NULL) {
|
|
|
|
goto LookupProcessWorkItemError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Copy in the Names to be looked up from the Work List. The Work
|
|
// Item contains their indices relative to the Names array in the
|
|
// Work List.
|
|
//
|
|
|
|
for (NextLevelIndex = 0;
|
|
NextLevelIndex < NextLevelCount;
|
|
NextLevelIndex++) {
|
|
|
|
Index = WorkItem->Indices[ NextLevelIndex ];
|
|
NextLevelNames[NextLevelIndex] =
|
|
*((PUNICODE_STRING) &WorkList->LookupNamesParams.Names[Index]);
|
|
}
|
|
|
|
NextLevelMappedCount = (ULONG) 0;
|
|
|
|
//
|
|
// Lookup the Names at the DC.
|
|
//
|
|
|
|
Status = LsaICLookupNames(
|
|
ControllerPolicyHandle,
|
|
NextLevelCount,
|
|
NextLevelNames,
|
|
(PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
|
|
(PLSA_TRANSLATED_SID *) &NextLevelTranslatedSids,
|
|
WorkList->LookupLevel,
|
|
&NextLevelMappedCount
|
|
);
|
|
|
|
//
|
|
// If the callout to LsaLookupNames() was unsuccessful, disregard
|
|
// the error and set the domain name for any Sids having this
|
|
// domain Sid as prefix sid.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto LookupProcessWorkItemError;
|
|
}
|
|
|
|
//
|
|
// The callout to LsaICLookupNames() was successful. Update the
|
|
// TranslatedSids information in the Work List as appropriate
|
|
// using the TranslatedSids information returned from the callout.
|
|
//
|
|
|
|
Status = LsapDbLookupNamesUpdateTranslatedSids(
|
|
WorkList,
|
|
WorkItem,
|
|
NextLevelTranslatedSids
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupProcessWorkItemError;
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto LookupProcessWorkItemError;
|
|
}
|
|
|
|
LookupProcessWorkItemFinish:
|
|
|
|
//
|
|
// Change the state of the work item to "Completed"
|
|
//
|
|
|
|
WorkItem->State = CompletedWorkItem;
|
|
|
|
|
|
//
|
|
// Update the Mapped Counts
|
|
//
|
|
|
|
LsapDbUpdateMappedCountsWorkList( WorkList );
|
|
|
|
|
|
//
|
|
// Protect WorkList operations
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto LookupProcessWorkItemError;
|
|
}
|
|
|
|
//
|
|
// Increment the count of completed Work Items whether or not this
|
|
// one was completed without error. If the Work List has just been
|
|
// completed, change its state to "CompletedWorkList" and signal
|
|
// the Lookup operation completed event. Allow re-entry into
|
|
// this section if an error is returned.
|
|
//
|
|
|
|
WorkList->CompletedWorkItemCount++;
|
|
|
|
|
|
LsapDiagPrint( DB_LOOKUP_WORK_LIST,
|
|
("LSA DB: Process Work Item Completed.\n"
|
|
" Item: (0x%lx, 0x%lx)\n"
|
|
" Completed Count: %ld\n", WorkList, WorkItem, WorkList->CompletedWorkItemCount));
|
|
|
|
if (WorkList->State != CompletedWorkList) {
|
|
|
|
if (WorkList->CompletedWorkItemCount == WorkList->WorkItemCount) {
|
|
|
|
WorkList->State = CompletedWorkList;
|
|
|
|
SecondaryStatus = LsapDbLookupSignalCompletionWorkList( WorkList );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Done making work list changes
|
|
//
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
|
|
|
|
//
|
|
// If necessary, close the Controller Policy Handle.
|
|
//
|
|
|
|
if (ControllerPolicyHandle != NULL) {
|
|
|
|
SecondaryStatus = LsaClose( ControllerPolicyHandle );
|
|
ControllerPolicyHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the array of Sids looked up at the next level.
|
|
//
|
|
|
|
if (NextLevelSids != NULL) {
|
|
|
|
MIDL_user_free( NextLevelSids );
|
|
NextLevelSids = NULL;
|
|
}
|
|
|
|
//
|
|
// If necessary, free the array of Names looked up at the next level.
|
|
//
|
|
|
|
if (NextLevelNames != NULL) {
|
|
MIDL_user_free( NextLevelNames );
|
|
NextLevelNames = NULL;
|
|
}
|
|
|
|
if (NextLevelReferencedDomains != NULL) {
|
|
MIDL_user_free( NextLevelReferencedDomains );
|
|
NextLevelReferencedDomains = NULL;
|
|
}
|
|
|
|
if (NextLevelTranslatedNames != NULL) {
|
|
MIDL_user_free( NextLevelTranslatedNames );
|
|
NextLevelTranslatedNames = NULL;
|
|
}
|
|
|
|
if (NextLevelTranslatedSids != NULL) {
|
|
MIDL_user_free( NextLevelTranslatedSids );
|
|
NextLevelTranslatedSids = NULL;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupProcessWorkItemError:
|
|
|
|
goto LookupProcessWorkItemFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupSidsUpdateTranslatedNames(
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
|
|
IN PLSA_TRANSLATED_NAME TranslatedNames
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called during the processing of a Work Item to update
|
|
the output TranslatedNames and ReferencedDomains parameters of a
|
|
Lookup operation's Work List with the results of a callout to
|
|
LsaICLookupNames. Zero or more Sids may have been translated. Note
|
|
that, unlike the translation of Names, Sid translation occurs within
|
|
a single Work Item only.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List
|
|
|
|
WorkItem - Pointer to a Work Item.
|
|
|
|
TranslatedNames - Translated Sids information returned from
|
|
LsaICLookupSids().
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Index, WorkItemIndex;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
PLSAPR_TRANSLATED_NAME WorkListTranslatedNames =
|
|
WorkList->LookupSidsParams.TranslatedNames->Names;
|
|
|
|
//
|
|
// Acquire the Work Queue Lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsUpdateTranslatedNamesError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
for( WorkItemIndex = 0;
|
|
WorkItemIndex < WorkItem->UsedCount;
|
|
WorkItemIndex++) {
|
|
|
|
//
|
|
// If this Sid has been fully translated, copy information to output.
|
|
// Note that the Sid is partially translated during the building
|
|
// phase where its Domain is identified.
|
|
//
|
|
|
|
if (TranslatedNames[WorkItemIndex].Use != SidTypeUnknown) {
|
|
|
|
Index = WorkItem->Indices[WorkItemIndex];
|
|
|
|
WorkListTranslatedNames[Index].Use
|
|
= TranslatedNames[WorkItemIndex].Use;
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
NULL,
|
|
(PUNICODE_STRING) &WorkListTranslatedNames[Index].Name,
|
|
(PUNICODE_STRING) &TranslatedNames[WorkItemIndex].Name
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupSidsUpdateTranslatedNamesError;
|
|
}
|
|
|
|
LookupSidsUpdateTranslatedNamesFinish:
|
|
|
|
//
|
|
// If necessary, release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupSidsUpdateTranslatedNamesError:
|
|
|
|
goto LookupSidsUpdateTranslatedNamesFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupNamesUpdateTranslatedSids(
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
|
|
IN PLSA_TRANSLATED_SID TranslatedSids
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called during the processing of a Work Item to update
|
|
the output TranslatedSids and ReferencedDomains parameters of a
|
|
Lookup operation's Work List with the results of a callout to
|
|
LsaICLookupNames. Zero or more Names may have been translated, and
|
|
there is the additional complication of multiple translations of
|
|
Isolated Names as a result of their presence in more than one
|
|
Work Item. The following rules apply:
|
|
|
|
If the Name is a Qualified Name, it only belongs to the specified
|
|
Work Item, so it suffices to check that it has been mapped to a Sid.
|
|
|
|
If the Name is an Isolated Name, it belongs to all other Work Items,
|
|
so it may already have been translated during the processing of some
|
|
other Work Item. If the Name has previously been translated, the prior
|
|
translation stands and the present translation is discarded. If the
|
|
Name has not previously been translated, the Domain for this Work Item
|
|
is added to the Referenced Domain List and the newly obtained translation
|
|
is stored in the output TranslatedSids array in the Work List.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List
|
|
|
|
WorkItem - Pointer to a Work Item. The DomainIndex field will be
|
|
updated if the Domain specified by this Work Item is added to
|
|
the Referenced Domain List by this routine.
|
|
|
|
TranslatedSids - Translated Sids information returned from
|
|
LsaICLookupNames().
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG WorkItemIndex;
|
|
ULONG Index;
|
|
PLSA_TRANSLATED_SID WorkListTranslatedSids =
|
|
WorkList->LookupNamesParams.TranslatedSids->Sids;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Acquire the Work Queue Lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesUpdateTranslatedSidsError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
for( WorkItemIndex = 0;
|
|
WorkItemIndex < WorkItem->UsedCount;
|
|
WorkItemIndex++) {
|
|
|
|
//
|
|
// If this Name has not been translated at all during processing of
|
|
// this Work Item, skip to the next.
|
|
//
|
|
|
|
if (LsapDbCompletelyUnmappedSid(&TranslatedSids[WorkItemIndex])) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We partially or fully translated the Name during processing of
|
|
// this Work Item. If this Name has previously been fully translated
|
|
// during the processing of another Work Item, discard the new
|
|
// translation and skip to the next Name. Note that Qualified
|
|
// Names are always partially translated during the building
|
|
// of the Work List. Isolated Names are fully translated during
|
|
// the building phase if they are Domain Names.
|
|
//
|
|
|
|
Index = WorkItem->Indices[WorkItemIndex];
|
|
|
|
if ( WorkListTranslatedSids[ Index ].Use != SidTypeUnknown ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Name has been translated for the first time during the processing
|
|
// of this Work Item. If this Work Item does not specify a Domain
|
|
// Index, we need to add its Domain to the Referenced Domains List.
|
|
//
|
|
|
|
if (WorkItem->DomainIndex == LSA_UNKNOWN_INDEX) {
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
WorkList->ReferencedDomains,
|
|
&WorkItem->TrustInformation,
|
|
(PLONG) &WorkItem->DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now update the TranslatedSids array in the Work List.
|
|
|
|
WorkListTranslatedSids[Index] = TranslatedSids[WorkItemIndex];
|
|
WorkListTranslatedSids[Index].DomainIndex = WorkItem->DomainIndex;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupNamesUpdateTranslatedSidsError;
|
|
}
|
|
|
|
LookupNamesUpdateTranslatedSidsFinish:
|
|
|
|
//
|
|
// If necessary, release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupNamesUpdateTranslatedSidsError:
|
|
|
|
goto LookupNamesUpdateTranslatedSidsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupCreateWorkItem(
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN LONG DomainIndex,
|
|
IN ULONG MaximumEntryCount,
|
|
OUT PLSAP_DB_LOOKUP_WORK_ITEM *WorkItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a new Work Item for a name Lookup operation.
|
|
|
|
Arguments:
|
|
|
|
TrustInformation - Specifies the Name of the Trusted Domain
|
|
to which the Work Item relates. The Sid field may be NULL or
|
|
set to the corresponding Sid. The Trust Information is expected
|
|
to be in heap or global data.
|
|
|
|
DomainIndex - Specifies the Domain Index of this domain relative to
|
|
the Referenced Domain List for the Lookup operation specified
|
|
by the Work List.
|
|
|
|
MaximumEntryCount - Specifies the maximum number of entries that
|
|
this Work Item will initialiiy be able to contain.
|
|
|
|
WorkItem - Receives a pointer to an empty Work Item structure.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM OutputWorkItem = NULL;
|
|
PULONG OutputIndices = NULL;
|
|
ULONG InitialEntryCount;
|
|
|
|
//
|
|
// Allocate memory for the Work Item Header.
|
|
//
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
OutputWorkItem = MIDL_user_allocate(sizeof(LSAP_DB_LOOKUP_WORK_ITEM));
|
|
|
|
if (OutputWorkItem == NULL) {
|
|
|
|
goto LookupCreateWorkItemError;
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
OutputWorkItem,
|
|
sizeof(LSAP_DB_LOOKUP_WORK_ITEM)
|
|
);
|
|
|
|
//
|
|
// Initialize the fixed fields in the Work Item.
|
|
//
|
|
|
|
Status = LsapDbLookupInitializeWorkItem(OutputWorkItem);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupCreateWorkItemError;
|
|
}
|
|
|
|
//
|
|
// Initialize other fields from parameters.
|
|
//
|
|
|
|
//
|
|
// Copy the trusted domain information into the work item. The
|
|
// trust information may be NULL if this is the isolated names
|
|
// work item.
|
|
//
|
|
|
|
if (TrustInformation != NULL) {
|
|
|
|
OutputWorkItem->TrustInformation.Sid =
|
|
MIDL_user_allocate( RtlLengthSid(TrustInformation->Sid) );
|
|
|
|
if (OutputWorkItem->TrustInformation.Sid == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto LookupCreateWorkItemError;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
OutputWorkItem->TrustInformation.Sid,
|
|
TrustInformation->Sid,
|
|
RtlLengthSid(TrustInformation->Sid)
|
|
);
|
|
|
|
OutputWorkItem->TrustInformation.Name.MaximumLength = TrustInformation->Name.Length + sizeof(WCHAR);
|
|
OutputWorkItem->TrustInformation.Name.Buffer =
|
|
MIDL_user_allocate(TrustInformation->Name.Length + sizeof(WCHAR));
|
|
|
|
if (OutputWorkItem->TrustInformation.Name.Buffer == NULL ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto LookupCreateWorkItemError;
|
|
}
|
|
|
|
RtlCopyUnicodeString(
|
|
(PUNICODE_STRING) &OutputWorkItem->TrustInformation.Name,
|
|
(PUNICODE_STRING) &TrustInformation->Name
|
|
);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Create the Indices array in the Work Item.
|
|
//
|
|
|
|
InitialEntryCount = (MaximumEntryCount +
|
|
LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY) &
|
|
(~LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY);
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
OutputIndices = MIDL_user_allocate( InitialEntryCount * sizeof(ULONG) );
|
|
|
|
if (OutputIndices == NULL) {
|
|
|
|
goto LookupCreateWorkItemError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
OutputWorkItem->UsedCount = (ULONG) 0;
|
|
OutputWorkItem->MaximumCount = InitialEntryCount;
|
|
OutputWorkItem->Indices = OutputIndices;
|
|
OutputWorkItem->DomainIndex = DomainIndex;
|
|
|
|
LookupCreateWorkItemFinish:
|
|
|
|
//
|
|
// Return pointer to newly created Work Item or NULL.
|
|
//
|
|
|
|
*WorkItem = OutputWorkItem;
|
|
return(Status);
|
|
|
|
LookupCreateWorkItemError:
|
|
|
|
//
|
|
// Free memory allocated for Indices array.
|
|
//
|
|
|
|
if (OutputIndices != NULL) {
|
|
|
|
MIDL_user_free( OutputIndices );
|
|
OutputIndices = NULL;
|
|
}
|
|
|
|
//
|
|
// Free any memory allocated for the Work Item header.
|
|
//
|
|
|
|
if (OutputWorkItem != NULL) {
|
|
if (OutputWorkItem->TrustInformation.Sid != NULL) {
|
|
MIDL_user_free( OutputWorkItem->TrustInformation.Sid );
|
|
}
|
|
|
|
if (OutputWorkItem->TrustInformation.Name.Buffer != NULL) {
|
|
MIDL_user_free( OutputWorkItem->TrustInformation.Name.Buffer );
|
|
}
|
|
|
|
MIDL_user_free(OutputWorkItem);
|
|
OutputWorkItem = NULL;
|
|
}
|
|
|
|
goto LookupCreateWorkItemFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupAddIndicesToWorkItem(
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
|
|
IN ULONG Count,
|
|
IN PULONG Indices
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function adds an array of Sid or Name indices to a Work Item.
|
|
The indices specify Sids or Names within the Sids or Names arrays in
|
|
the WorkList. If there is sufficient room in the Work Item's
|
|
existing indices array, the indices will be copied to that array.
|
|
Otherwise, a larger array will be allocated and the existing one
|
|
will be freed after copying the existing indices.
|
|
|
|
NOTE: The Work Item must NOT belong to a Work List that is currently
|
|
on the Work Queue. The Lookup Work Queue Lock will not be
|
|
taken.
|
|
Arguments:
|
|
|
|
WorkItem - Pointer to a Work Item structure describing a
|
|
list of Sids or Names and a domain in which they are to be looked up.
|
|
|
|
Count - Specifies the number of indices to be added.
|
|
|
|
Indices - Specifies the array of indices to be added to the WorkItem.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PULONG OutputIndices = NULL;
|
|
ULONG NewMaximumCount;
|
|
|
|
//
|
|
// Check room available in the work item. If there is enough
|
|
// room, just copy the indices in.
|
|
//
|
|
|
|
if (WorkItem->MaximumCount - WorkItem->UsedCount >= Count) {
|
|
|
|
RtlCopyMemory(
|
|
&WorkItem->Indices[WorkItem->UsedCount],
|
|
Indices,
|
|
Count * sizeof(ULONG)
|
|
);
|
|
|
|
WorkItem->UsedCount += Count;
|
|
goto AddIndicesToWorkItemFinish;
|
|
}
|
|
|
|
//
|
|
// Allocate array of sufficient size to accommodate the existing
|
|
// and new indices. Round up number of entries to some granularity
|
|
// to avoid frequent reallocations.
|
|
//
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
NewMaximumCount = ((WorkItem->UsedCount + Count) +
|
|
LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY) &
|
|
(~LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY);
|
|
|
|
OutputIndices = MIDL_user_allocate( NewMaximumCount * sizeof(ULONG) );
|
|
|
|
if (OutputIndices == NULL) {
|
|
|
|
goto AddIndicesToWorkItemError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Copy in the existing and new indices.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
OutputIndices,
|
|
WorkItem->Indices,
|
|
WorkItem->UsedCount * sizeof(ULONG)
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
&OutputIndices[WorkItem->UsedCount],
|
|
Indices,
|
|
Count * sizeof(ULONG)
|
|
);
|
|
|
|
//
|
|
// Free the existing indices. Set pointer to the updated indices array
|
|
// and update the used and maximum counts.
|
|
//
|
|
|
|
MIDL_user_free( WorkItem->Indices );
|
|
WorkItem->Indices = OutputIndices;
|
|
WorkItem->UsedCount += Count;
|
|
WorkItem->MaximumCount = NewMaximumCount;
|
|
|
|
AddIndicesToWorkItemFinish:
|
|
|
|
return(Status);
|
|
|
|
AddIndicesToWorkItemError:
|
|
|
|
//
|
|
// Free any memory allocated for the Output Indices array.
|
|
//
|
|
|
|
if (OutputIndices != NULL) {
|
|
|
|
MIDL_user_free( OutputIndices );
|
|
OutputIndices = NULL;
|
|
}
|
|
|
|
goto AddIndicesToWorkItemFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupComputeAdvisoryChildThreadCount(
|
|
IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function computes the advisory thread count for a Lookup
|
|
operation. This count is an estimate of the optimal number of
|
|
worker threads (in addition to the main thread) needed to process the
|
|
Work List.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List structure describing a
|
|
Lookup Sids or Lookup Names operation.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ASSERT(WorkList->WorkItemCount != (ULONG) 0);
|
|
WorkList->AdvisoryChildThreadCount = (WorkList->WorkItemCount - (ULONG) 1);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupUpdateAssignableWorkItem(
|
|
IN BOOLEAN MoveToNextWorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function updates the next assignable Work Item pointers.
|
|
|
|
Arguments:
|
|
|
|
MoveToNextWorkList - If TRUE, skip the rest of the current Work List. If
|
|
FALSE, point at the next item in the current Work List.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM CandAssignableWorkItem = NULL;
|
|
PLSAP_DB_LOOKUP_WORK_LIST CandAssignableWorkList = NULL;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Acquire the LookupWork Queue Lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupUpdateAssignableWorkItemError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
//
|
|
// If there is no Currently Assignable Work List, just exit.
|
|
//
|
|
|
|
if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {
|
|
|
|
goto LookupUpdateAssignableWorkItemFinish;
|
|
}
|
|
|
|
//
|
|
// There is a Currently Assignable Work List. Unless requested to
|
|
// skip this Work List, examine it.
|
|
//
|
|
|
|
if (!MoveToNextWorkList) {
|
|
|
|
ASSERT( LookupWorkQueue.CurrentAssignableWorkItem != NULL);
|
|
|
|
//
|
|
// Select the next Work Item in the list as candidate for the
|
|
// next Assignable Work Item. If we have not returned to the First
|
|
// Work Item, selection is complete.
|
|
//
|
|
|
|
CandAssignableWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
|
|
LookupWorkQueue.CurrentAssignableWorkItem->Links.Flink;
|
|
|
|
if (CandAssignableWorkItem !=
|
|
LookupWorkQueue.CurrentAssignableWorkList->AnchorWorkItem) {
|
|
|
|
ASSERT( CandAssignableWorkItem->State == AssignableWorkItem);
|
|
|
|
LookupWorkQueue.CurrentAssignableWorkItem = CandAssignableWorkItem;
|
|
goto LookupUpdateAssignableWorkItemFinish;
|
|
}
|
|
}
|
|
|
|
//
|
|
// There are no more work items in this Work List or we're to skip the
|
|
// rest of it. See if there is another Work List.
|
|
//
|
|
|
|
CandAssignableWorkList = (PLSAP_DB_LOOKUP_WORK_LIST)
|
|
LookupWorkQueue.CurrentAssignableWorkList->WorkLists.Flink;
|
|
|
|
if (CandAssignableWorkList != LookupWorkQueue.AnchorWorkList) {
|
|
|
|
//
|
|
// There is another Work List. Select the first Work Item in the
|
|
// list following the anchor.
|
|
//
|
|
|
|
CandAssignableWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
|
|
CandAssignableWorkList->AnchorWorkItem->Links.Flink;
|
|
|
|
//
|
|
// Verify that the list does not just contain the Anchor Work Item.
|
|
// Work Lists on the Work Queue should never be empty.
|
|
//
|
|
|
|
ASSERT (CandAssignableWorkItem != CandAssignableWorkList->AnchorWorkItem);
|
|
|
|
LookupWorkQueue.CurrentAssignableWorkList = CandAssignableWorkList;
|
|
LookupWorkQueue.CurrentAssignableWorkItem = CandAssignableWorkItem;
|
|
goto LookupUpdateAssignableWorkItemFinish;
|
|
}
|
|
|
|
//
|
|
// All work has been assigned. Set pointers to NULL.
|
|
//
|
|
|
|
LookupWorkQueue.CurrentAssignableWorkList = NULL;
|
|
LookupWorkQueue.CurrentAssignableWorkItem = NULL;
|
|
|
|
LookupUpdateAssignableWorkItemFinish:
|
|
|
|
//
|
|
// If necessary, release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupUpdateAssignableWorkItemError:
|
|
|
|
goto LookupUpdateAssignableWorkItemFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupStopProcessingWorkList(
|
|
IN PLSAP_DB_LOOKUP_WORK_LIST WorkList,
|
|
IN NTSTATUS TerminationStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function stops further work on a lookup operation at a given
|
|
level and stores an error code.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Pointer to a Work List structure describing a
|
|
Lookup Sids or Lookup Names operation.
|
|
|
|
TerminationStatus - Specifies the Nt Result Code to be returned
|
|
by LsarLookupnames or LsarLookupSids.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN AcquiredWorkQueueLock = FALSE;
|
|
|
|
//
|
|
// Acquire the LookupWork Queue Lock.
|
|
//
|
|
|
|
Status = LsapDbLookupAcquireWorkQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupStopProcessingWorkListError;
|
|
}
|
|
|
|
AcquiredWorkQueueLock = TRUE;
|
|
|
|
//
|
|
// Store the termination status in the appropriate WorkList.
|
|
//
|
|
|
|
WorkList->Status = TerminationStatus;
|
|
|
|
//
|
|
// If this WorkList happens to be the one in which Work Items are being
|
|
// given out, we need to prevent any further Work Items from being given
|
|
// out. Update the next assignable Work Item pointers, specifying that
|
|
// we should skip to the next Work List (if any).
|
|
//
|
|
|
|
if (WorkList == LookupWorkQueue.CurrentAssignableWorkList) {
|
|
|
|
Status = LsapDbLookupUpdateAssignableWorkItem(TRUE);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupStopProcessingWorkListError;
|
|
}
|
|
|
|
LookupStopProcessingWorkListFinish:
|
|
|
|
//
|
|
// If necessary, release the Lookup Work Queue Lock.
|
|
//
|
|
|
|
if (AcquiredWorkQueueLock) {
|
|
|
|
LsapDbLookupReleaseWorkQueueLock();
|
|
AcquiredWorkQueueLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupStopProcessingWorkListError:
|
|
|
|
goto LookupStopProcessingWorkListFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapRtlExtractDomainSid(
|
|
IN PSID Sid,
|
|
OUT PSID *DomainSid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function extracts a Domain Sid from a Sid.
|
|
|
|
Arguments:
|
|
|
|
Sid - Pointer to Sid whose Domain Prefix Sid is to be extracted.
|
|
|
|
DomainSid - Receives pointer to Domain Sid. This Sid will be
|
|
allocated memory by MIDL_User_allocate() and should be freed
|
|
via MIDL_user_free when no longer required.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
--*/
|
|
|
|
{
|
|
PSID OutputDomainSid;
|
|
ULONG DomainSidLength = RtlLengthSid(Sid) - sizeof(ULONG);
|
|
|
|
|
|
|
|
OutputDomainSid = MIDL_user_allocate( DomainSidLength );
|
|
if (OutputDomainSid == NULL) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
|
|
RtlCopyMemory( OutputDomainSid, Sid, DomainSidLength);
|
|
(*(RtlSubAuthorityCountSid(OutputDomainSid)))--;
|
|
|
|
*DomainSid = OutputDomainSid;
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupInitialize(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function performs initialization of the data structures
|
|
used by Lookup operations. These structures are as follows:
|
|
|
|
LookupWorkQueue - This is a doubly-linked list of Lookup Work Lists.
|
|
There is one Work List for each Lookup operation in progress
|
|
on a DC. Each Lookup Work List contains a doubly-linked list
|
|
of Lookup Work Items. Each Lookup Work Item specifies a
|
|
Trusted Domain and array of Sids or Names to be looked up in that
|
|
domain. Access to this queue is controlled via the Lookup
|
|
Work Queue Lock.
|
|
|
|
Trusted Domain List - This is a doubly-linked list which contains
|
|
the Trust Information (i.e. Domain Sid and Domain Name) of
|
|
each Trusted Domain. The purpose of this list is to enable
|
|
fast identification of Trusted Domain Sids and Names, without
|
|
having recourse to open or enumerate Trusted Domain objects.
|
|
This list is initialized when the system is loaded, and is
|
|
updated directly when a Trusted Domain object is created or
|
|
deleted.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Perform initialization specific to DC's
|
|
//
|
|
|
|
if (LsapProductType != NtProductLanManNt) {
|
|
|
|
goto LookupInitializeFinish;
|
|
}
|
|
|
|
//
|
|
// Create the Lookup Work List initiated event.
|
|
//
|
|
|
|
Status = NtCreateEvent(
|
|
&LsapDbLookupStartedEvent,
|
|
EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupInitializeError;
|
|
}
|
|
|
|
//
|
|
// Create the Lookup Work List complete event.
|
|
//
|
|
|
|
Status = NtCreateEvent(
|
|
&LsapDbLookupCompleteEvent,
|
|
EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupInitializeError;
|
|
}
|
|
|
|
//
|
|
// Initialize the Lookup Work Queue
|
|
//
|
|
|
|
Status = LsapDbLookupInitializeWorkQueue();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupInitializeError;
|
|
}
|
|
|
|
LookupInitializeFinish:
|
|
|
|
return(Status);
|
|
|
|
LookupInitializeError:
|
|
|
|
goto LookupInitializeFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupInitializeWorkQueue(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the Lookup Work Queue. It is only
|
|
called for DC's.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAP_DB_LOOKUP_WORK_LIST AnchorWorkList = NULL;
|
|
|
|
//
|
|
// Initialize the Work Queue Lock.
|
|
//
|
|
|
|
Status = RtlInitializeCriticalSection(&LookupWorkQueue.Lock);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
LsapLogError(
|
|
"LsapDbLookupInitialize: RtlInit..CritSec returned 0x%lx\n",
|
|
Status
|
|
);
|
|
}
|
|
|
|
//
|
|
// Initialize the Work Queue to comprise the Anchor Work List
|
|
// doubly-linked to itself.
|
|
//
|
|
|
|
LookupWorkQueue.AnchorWorkList = &LookupWorkQueue.DummyAnchorWorkList;
|
|
AnchorWorkList = &LookupWorkQueue.DummyAnchorWorkList;
|
|
|
|
//
|
|
// Set the currently assignable Work List and Work Item pointers to
|
|
// NULL to indicate that there is no work to to.
|
|
//
|
|
|
|
LookupWorkQueue.CurrentAssignableWorkList = NULL;
|
|
LookupWorkQueue.CurrentAssignableWorkItem = NULL;
|
|
|
|
//
|
|
// Initialize the Anchor Work List.
|
|
//
|
|
|
|
Status = LsapDbLookupInitializeWorkList(AnchorWorkList);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupInitializeWorkQueueError;
|
|
}
|
|
|
|
AnchorWorkList->WorkLists.Flink = (PLIST_ENTRY) AnchorWorkList;
|
|
AnchorWorkList->WorkLists.Blink = (PLIST_ENTRY) AnchorWorkList;
|
|
|
|
//
|
|
// Set the thread counts.
|
|
//
|
|
|
|
LookupWorkQueue.ActiveChildThreadCount = (ULONG) 0;
|
|
LookupWorkQueue.MaximumChildThreadCount = LSAP_DB_LOOKUP_MAX_THREAD_COUNT;
|
|
LookupWorkQueue.MaximumRetainedChildThreadCount = LSAP_DB_LOOKUP_MAX_RET_THREAD_COUNT;
|
|
|
|
LookupInitializeWorkQueueFinish:
|
|
|
|
return(Status);
|
|
|
|
LookupInitializeWorkQueueError:
|
|
|
|
goto LookupInitializeWorkQueueFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupInitializeWorkList(
|
|
OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes an empty Work List structure. The Work List
|
|
link fields are not set by this function.
|
|
|
|
Arguments:
|
|
|
|
WorkList - Points to Work List structure to be initialized.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
|
|
|
|
//
|
|
// Initialize miscellaneous fields in the Work List header.
|
|
//
|
|
|
|
WorkList->WorkLists.Flink = NULL;
|
|
WorkList->WorkLists.Blink = NULL;
|
|
WorkList->WorkItemCount = (ULONG) 0;
|
|
WorkList->CompletedWorkItemCount = (ULONG) 0;
|
|
WorkList->State = InactiveWorkList;
|
|
WorkList->Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Initialize the Work List's list of Work Items to comprise the
|
|
// Anchor Work Item doubly-linked to itself.
|
|
//
|
|
|
|
WorkList->AnchorWorkItem = &WorkList->DummyAnchorWorkItem;
|
|
AnchorWorkItem = WorkList->AnchorWorkItem;
|
|
AnchorWorkItem->Links.Flink = (PLIST_ENTRY) AnchorWorkItem;
|
|
AnchorWorkItem->Links.Blink = (PLIST_ENTRY) AnchorWorkItem;
|
|
|
|
//
|
|
// Initialize the Anchor Work Item.
|
|
//
|
|
|
|
Status = LsapDbLookupInitializeWorkItem(AnchorWorkItem);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupInitializeWorkListError;
|
|
}
|
|
|
|
LookupInitializeWorkListFinish:
|
|
|
|
return(Status);
|
|
|
|
LookupInitializeWorkListError:
|
|
|
|
goto LookupInitializeWorkListFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupInitializeWorkItem(
|
|
OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes an empty Work Item structure. The Work Item
|
|
link fields are not set by this function.
|
|
|
|
Arguments:
|
|
|
|
WorkItem - Points to Work Item structure to be initialized.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
WorkItem->UsedCount = (ULONG) 0;
|
|
WorkItem->MaximumCount = (ULONG) 0;
|
|
WorkItem->State = NonAssignableWorkItem;
|
|
WorkItem->Properties = (ULONG) 0;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupLocalDomains(
|
|
OUT PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
|
|
OUT PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
|
|
OUT PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns Trust Information for the Local Domains.
|
|
|
|
Arguments:
|
|
|
|
BuiltInDomainTrustInformation - Pointer to structure that will
|
|
receive the Name and Sid of the Built-In Domain. Unlike
|
|
the other two parameters, the Name and Sid buffers for the
|
|
Built-in Domain are NOT freed after use, because they are
|
|
global data constants.
|
|
|
|
AccountDomainTrustInformation - Pointer to structure that will
|
|
receive the Name and Sid of the Accounts Domain. The Name and
|
|
Sid buffers must be freed after use via MIDL_user_free.
|
|
|
|
PrimaryDomainTrustInformation - Pointer to structure that will
|
|
receive the Name and Sid of the Accounts Domain. The Name and
|
|
Sid buffers must be freed after use via MIDL_user_free.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG WellKnownSidIndex;
|
|
PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;
|
|
PLSAPR_POLICY_PRIMARY_DOM_INFO PolicyPrimaryDomainInfo = NULL;
|
|
|
|
//
|
|
// Obtain the Name and Sid of the Built-in Domain
|
|
//
|
|
|
|
BuiltInDomainTrustInformation->Sid = LsapBuiltInDomainSid;
|
|
|
|
Status = STATUS_INTERNAL_DB_ERROR;
|
|
|
|
if (!LsapDbLookupIndexWellKnownSid(
|
|
LsapBuiltInDomainSid,
|
|
(PLSAP_WELL_KNOWN_SID_INDEX) &WellKnownSidIndex
|
|
)) {
|
|
|
|
goto LookupLocalDomainsError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Obtain the name of the Built In Domain from the table of
|
|
// Well Known Sids. It suffices to copy the Unicode structures
|
|
// since we do not need a separate copy of the name buffer.
|
|
//
|
|
|
|
BuiltInDomainTrustInformation->Name = *((PLSAPR_UNICODE_STRING)
|
|
LsapDbWellKnownSidName(WellKnownSidIndex));
|
|
|
|
//
|
|
// Now obtain the Name and Sid of the Account Domain.
|
|
// The Sid and Name of the Account Domain are both configurable, and
|
|
// we need to obtain them from the Policy Object. Now obtain the
|
|
// Account Domain Sid and Name by querying the appropriate
|
|
// Policy Information Class.
|
|
//
|
|
|
|
Status = LsarQueryInformationPolicy(
|
|
LsapPolicyHandle,
|
|
PolicyAccountDomainInformation,
|
|
(PLSAPR_POLICY_INFORMATION *) &PolicyAccountDomainInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupLocalDomainsError;
|
|
}
|
|
|
|
//
|
|
// Set up the Trust Information structure for the Account Domain.
|
|
//
|
|
|
|
AccountDomainTrustInformation->Sid = PolicyAccountDomainInfo->DomainSid;
|
|
|
|
RtlCopyMemory(
|
|
&AccountDomainTrustInformation->Name,
|
|
&PolicyAccountDomainInfo->DomainName,
|
|
sizeof (UNICODE_STRING)
|
|
);
|
|
|
|
//
|
|
// Now obtain the Name and Sid of the Primary Domain (if any)
|
|
// The Sid and Name of the Primary Domain are both configurable, and
|
|
// we need to obtain them from the Policy Object. Now obtain the
|
|
// Account Domain Sid and Name by querying the appropriate
|
|
// Policy Information Class.
|
|
//
|
|
|
|
Status = LsarQueryInformationPolicy(
|
|
LsapPolicyHandle,
|
|
PolicyPrimaryDomainInformation,
|
|
(PLSAPR_POLICY_INFORMATION *) &PolicyPrimaryDomainInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupLocalDomainsError;
|
|
}
|
|
|
|
//
|
|
// Set up the Trust Information structure for the Primary Domain.
|
|
//
|
|
|
|
PrimaryDomainTrustInformation->Sid = PolicyPrimaryDomainInfo->Sid;
|
|
|
|
RtlCopyMemory(
|
|
&PrimaryDomainTrustInformation->Name,
|
|
&PolicyPrimaryDomainInfo->Name,
|
|
sizeof (UNICODE_STRING)
|
|
);
|
|
|
|
LookupLocalDomainsFinish:
|
|
|
|
//
|
|
// If allocated, free up the top-level structures returned by
|
|
// LsarQueryInformationPolicy.
|
|
//
|
|
|
|
if (PolicyPrimaryDomainInfo != NULL) {
|
|
|
|
MIDL_user_free(PolicyPrimaryDomainInfo);
|
|
}
|
|
|
|
if (PolicyAccountDomainInfo != NULL) {
|
|
|
|
MIDL_user_free(PolicyAccountDomainInfo);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
LookupLocalDomainsError:
|
|
|
|
goto LookupLocalDomainsFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbLookupIsolatedDomainName(
|
|
IN ULONG NameIndex,
|
|
IN PLSAPR_UNICODE_STRING IsolatedName,
|
|
IN PLSAPR_TRUST_INFORMATION TrustInformation,
|
|
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
|
|
IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG CompletelyUnmappedCount,
|
|
IN OUT PULONG MappedIsolatedNameCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function translates an Isolated Name if it matches a
|
|
given Domain Name.
|
|
|
|
Arguments:
|
|
|
|
NameIndex - Specifies the index of the entry for the Name within
|
|
the TranslatedSids array, which will be updated if the Name
|
|
matches the Domain Name contained in the TrusteInformation parameter.
|
|
|
|
IsolatedName - Specifies the Name to be compared with the Domain Name
|
|
contained in the TrustInformation parameter.
|
|
|
|
TrustInformation - Specifies the Name and Sid of a Domain.
|
|
|
|
MappedIsolatedNameCount - Pointer to a location in which a count
|
|
of the Isolated Names that have been mapped is maintained.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_NONE_MAPPED - The specified name is not the same as the
|
|
name of the specified domain.
|
|
|
|
Result codes from called routines.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_NONE_MAPPED;
|
|
|
|
//
|
|
// See if the names match. If they don't, return error.
|
|
//
|
|
|
|
if (!RtlEqualDomainName(
|
|
(PUNICODE_STRING) IsolatedName,
|
|
(PUNICODE_STRING) &TrustInformation->Name
|
|
)) {
|
|
|
|
goto LookupIsolatedDomainNameError;
|
|
}
|
|
|
|
//
|
|
// Name matches the name of the given Domain. Add that
|
|
// Domain to the Referenced Domain List and translate it.
|
|
//
|
|
|
|
Status = LsapDbLookupAddListReferencedDomains(
|
|
ReferencedDomains,
|
|
TrustInformation,
|
|
(PLONG) &TranslatedSids->Sids[NameIndex].DomainIndex
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto LookupIsolatedDomainNameError;
|
|
}
|
|
|
|
//
|
|
// Fill in the Translated Sids entry.
|
|
//
|
|
|
|
TranslatedSids->Sids[NameIndex].Use = SidTypeDomain;
|
|
TranslatedSids->Sids[NameIndex].RelativeId = LSA_UNKNOWN_ID;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
(*MappedCount)++;
|
|
(*CompletelyUnmappedCount)--;
|
|
(*MappedIsolatedNameCount)++;
|
|
|
|
LookupIsolatedDomainNameFinish:
|
|
|
|
return(Status);
|
|
|
|
LookupIsolatedDomainNameError:
|
|
|
|
goto LookupIsolatedDomainNameFinish;
|
|
}
|
|
|
|
NTSTATUS
|
|
LsarGetUserName(
|
|
IN PLSAPR_SERVER_NAME ServerName,
|
|
OUT PLSAPR_UNICODE_STRING * UserName,
|
|
OUT OPTIONAL PLSAPR_UNICODE_STRING * DomainName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the LSA Server worker routine for the LsaGetUserName
|
|
API.
|
|
|
|
|
|
WARNING: This routine allocates memory for its output. The caller is
|
|
responsible for freeing this memory after use. See description of the
|
|
Names parameter.
|
|
|
|
Arguments:
|
|
|
|
ServerName - the name of the server the client asked to execute
|
|
this API on, or NULL for the local machine.
|
|
|
|
UserName - Receives name of the current user.
|
|
|
|
DomainName - Optioanlly receives domain name of the current user.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully and all Sids have
|
|
been translated to names.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
such as memory to complete the call.
|
|
--*/
|
|
|
|
{
|
|
LUID LogonId;
|
|
PUNICODE_STRING AccountName;
|
|
PUNICODE_STRING AuthorityName;
|
|
PSID UserSid;
|
|
PSID DomainSid = NULL;
|
|
ULONG Rid;
|
|
PLSAP_LOGON_SESSION LogonSession;
|
|
PTOKEN_USER TokenUserInformation = NULL;
|
|
NTSTATUS Status;
|
|
BOOLEAN LockHeld = FALSE;
|
|
|
|
*UserName = NULL;
|
|
if (ARGUMENT_PRESENT(DomainName)) {
|
|
*DomainName = NULL;
|
|
}
|
|
|
|
//
|
|
// Let's see if we're trying to look up the currently logged on
|
|
// user.
|
|
//
|
|
//
|
|
// TokenUserInformation from this call must be freed by calling
|
|
// LsapFreeLsaHeap().
|
|
//
|
|
|
|
Status = LsapQueryClientInfo(
|
|
&TokenUserInformation,
|
|
&LogonId
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If the user ID is Anonymous then there is no name and domain in the
|
|
// logon session
|
|
//
|
|
|
|
if (RtlEqualSid(
|
|
TokenUserInformation->User.Sid,
|
|
LsapAnonymousSid
|
|
)) {
|
|
AccountName = &WellKnownSids[LsapAnonymousSidIndex].Name;
|
|
AuthorityName = &WellKnownSids[LsapAnonymousSidIndex].DomainName;
|
|
|
|
} else {
|
|
|
|
LsapAuLock();
|
|
LockHeld = TRUE;
|
|
|
|
LogonSession = LsapGetLogonSession ( &LogonId, FALSE );
|
|
|
|
//
|
|
// During setup, we may get NULL returned for the logon session.
|
|
//
|
|
|
|
if (LogonSession == NULL) {
|
|
|
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Got a match. Get the username and domain information
|
|
// from the LogonId
|
|
//
|
|
|
|
|
|
AccountName = LogonSession->AccountName;
|
|
AuthorityName = LogonSession->AuthorityName;
|
|
|
|
}
|
|
|
|
*UserName = MIDL_user_allocate(sizeof(LSAPR_UNICODE_STRING));
|
|
|
|
if (*UserName == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
NULL,
|
|
(PUNICODE_STRING) *UserName,
|
|
AccountName
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Optionally copy the domain name
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(DomainName)) {
|
|
|
|
*DomainName = MIDL_user_allocate(sizeof(LSAPR_UNICODE_STRING));
|
|
|
|
if (*DomainName == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
NULL,
|
|
(PUNICODE_STRING) *DomainName,
|
|
AuthorityName
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Cleanup:
|
|
|
|
if (LockHeld) {
|
|
LsapAuUnlock();
|
|
}
|
|
|
|
if (TokenUserInformation != NULL) {
|
|
LsapFreeLsaHeap( TokenUserInformation );
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (*UserName != NULL) {
|
|
if ((*UserName)->Buffer != NULL) {
|
|
MIDL_user_free((*UserName)->Buffer);
|
|
}
|
|
MIDL_user_free(*UserName);
|
|
*UserName = NULL;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(DomainName) ){
|
|
if (*DomainName != NULL) {
|
|
if ((*DomainName)->Buffer != NULL) {
|
|
MIDL_user_free((*DomainName)->Buffer);
|
|
}
|
|
MIDL_user_free(*DomainName);
|
|
*DomainName = NULL;
|
|
}
|
|
}
|
|
}
|
|
return(Status);
|
|
}
|
|
|