Leaked source code of windows server 2003
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.
 
 
 
 
 
 

6039 lines
181 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 <lsapch2.h>
#include "dbp.h"
#include <sidcache.h>
#include <bndcache.h>
#include <malloc.h>
#include <ntdsa.h>
#include <ntdsapi.h>
#include <ntdsapip.h>
#include "lsawmi.h"
#include <lmapibuf.h>
#include <dsgetdc.h>
//////////////////////////////////////////////////////////////////////////
// //
// Lsa Lookup Sid and Name Private Global State Variables //
// //
//////////////////////////////////////////////////////////////////////////
//
// See comment in dbluutil.c
//
extern BOOLEAN LsapReturnSidTypeDeleted;
//
// The shortcut list is meant for well known security principals
// whose SidTypeUse is WellKnownGroup, not User
//
struct {
LUID LogonId;
LSAP_WELL_KNOWN_SID_INDEX LookupIndex;
} LsapShortcutLookupList[] = {
{ SYSTEM_LUID, LsapLocalSystemSidIndex },
{ ANONYMOUS_LOGON_LUID, LsapAnonymousSidIndex },
{ LOCALSERVICE_LUID, LsapLocalServiceSidIndex },
{ NETWORKSERVICE_LUID, LsapNetworkServiceSidIndex }
};
//
// Handy macros for iterating over static arrays
//
#define NELEMENTS(x) (sizeof(x)/sizeof(x[0]))
NTSTATUS
LsapDbLookupSidsInTrustedForests(
IN ULONG Count,
IN PLSAPR_SID *Sids,
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
IN OUT PULONG MappedCount,
IN OUT PULONG CompletelyUnmappedCount,
OUT NTSTATUS *NonFatalStatus
);
NTSTATUS
LsapDbLookupSidsInTrustedForestsWorker(
IN ULONG Count,
IN PLSAPR_SID *Sids,
OUT PLSAPR_REFERENCED_DOMAIN_LIST * ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
OUT BOOLEAN *fAllocateAllNodes,
IN OUT PULONG MappedCount,
OUT NTSTATUS *NonFatalStatus
);
NTSTATUS
LsapLookupSids(
IN LSAPR_HANDLE PolicyHandle,
IN PLSAPR_SID_ENUM_BUFFER SidEnumBuffer,
OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
IN LSAP_LOOKUP_LEVEL LookupLevel,
IN OUT PULONG MappedCount,
IN ULONG LookupOptions,
IN ULONG ClientRevision
);
NTSTATUS
LsapDomainHasForestTrust(
IN PUNICODE_STRING DomainName, OPTIONAL
IN PSID DomainSid, OPTIONAL
IN OUT BOOLEAN *fTDLLock, OPTIONAL
OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY *TrustEntryOut OPTIONAL
);
NTSTATUS
LsapDomainHasDirectTrust(
IN PUNICODE_STRING DomainName, OPTIONAL
IN PSID DomainSid, OPTIONAL
IN OUT BOOLEAN *fTDLLock, OPTIONAL
OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY *TrustEntryOut OPTIONAL
);
NTSTATUS
LsapDomainHasTransitiveTrust(
IN PUNICODE_STRING DomainName, OPTIONAL
IN PSID DomainSid, OPTIONAL
OUT LSA_TRUST_INFORMATION *TrustInfo OPTIONAL
);
NTSTATUS
LsapDomainHasDirectExternalTrust(
IN PUNICODE_STRING DomainName, OPTIONAL
IN PSID DomainSid, OPTIONAL
IN OUT BOOLEAN *fTDLLock, OPTIONAL
OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY *TrustEntryOut OPTIONAL
);
//////////////////////////////////////////////////////////////////////////
// //
// Lsa Lookup Sid Routines //
// //
//////////////////////////////////////////////////////////////////////////
NTSTATUS
LsarLookupSids(
IN LSAPR_HANDLE PolicyHandle,
IN PLSAPR_SID_ENUM_BUFFER SidEnumBuffer,
OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
IN LSAP_LOOKUP_LEVEL LookupLevel,
IN OUT PULONG MappedCount
)
/*++
Routine Description:
See LsapLookupSids. LsarLookupSids is called by NT4 and below clients
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG Size;
LSAPR_TRANSLATED_NAMES_EX TranslatedNamesEx = {0, NULL};
LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsarLookupSids(%ws) start\n", LsapDbLookupGetLevel(LookupLevel)) );
//
// Note that due to the IN/OUT nature of TranslatedSids, it is
// possible that a client can pass something into the Sids field.
// However, NT clients do not so it is safe, and correct to free
// any values at this point. Not doing so would mean a malicious
// client could cause starve the server.
//
if ( TranslatedNames->Names ) {
MIDL_user_free( TranslatedNames->Names );
TranslatedNames->Names = NULL;
}
//
// Allocate the TranslatedName buffer to return
//
TranslatedNames->Entries = 0;
Size = SidEnumBuffer->Entries * sizeof(LSAPR_TRANSLATED_NAME);
TranslatedNames->Names = midl_user_allocate( Size );
if ( !TranslatedNames->Names ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlZeroMemory( TranslatedNames->Names, Size );
TranslatedNames->Entries = SidEnumBuffer->Entries;
Status = LsapLookupSids( PolicyHandle,
SidEnumBuffer,
ReferencedDomains,
(PLSAPR_TRANSLATED_NAMES_EX) &TranslatedNamesEx,
LookupLevel,
MappedCount,
0,
LSA_CLIENT_PRE_NT5 );
if ( TranslatedNamesEx.Names != NULL ) {
//
// Map the new data structure back to the old one
//
ULONG i;
ASSERT( TranslatedNamesEx.Entries == TranslatedNames->Entries );
for (i = 0; i < TranslatedNamesEx.Entries; i++ ) {
if (LsapReturnSidTypeDeleted
&& TranslatedNamesEx.Names[i].Use == SidTypeUnknown
&& TranslatedNamesEx.Names[i].DomainIndex != LSA_UNKNOWN_INDEX) {
//
// A domain was found, but the SID couldn't be resolved
// assume it has been deleted
//
TranslatedNames->Names[i].Use = SidTypeDeletedAccount;
} else {
TranslatedNames->Names[i].Use = TranslatedNamesEx.Names[i].Use;
}
TranslatedNames->Names[i].Name = TranslatedNamesEx.Names[i].Name;
TranslatedNames->Names[i].DomainIndex = TranslatedNamesEx.Names[i].DomainIndex;
}
//
// Free the Ex structure
//
midl_user_free( TranslatedNamesEx.Names );
} else {
TranslatedNames->Entries = 0;
midl_user_free( TranslatedNames->Names );
TranslatedNames->Names = NULL;
}
Cleanup:
LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsarLookupSids(%ws) end (0x%x)\n", LsapDbLookupGetLevel(LookupLevel), Status) );
return Status;
}
NTSTATUS
LsarLookupSids2(
IN LSAPR_HANDLE PolicyHandle,
IN PLSAPR_SID_ENUM_BUFFER SidEnumBuffer,
OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
IN LSAP_LOOKUP_LEVEL LookupLevel,
IN OUT PULONG MappedCount,
IN ULONG LookupOptions,
IN ULONG ClientRevision
)
/*++
Routine Description:
This routine is the server entry point for the IDL LsarLookupSids2.
See LsapLookupSids. This API is used by win2k clients.
Arguments:
RpcHandle -- an RPC binding handle
Rest -- See LsarLookupSids2
Return Values:
See LsarLookupSids2
--*/
{
NTSTATUS Status;
LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsarLookupSids2(%ws) start\n", LsapDbLookupGetLevel(LookupLevel)) );
Status = LsapLookupSids(PolicyHandle,
SidEnumBuffer,
ReferencedDomains,
TranslatedNames,
LookupLevel,
MappedCount,
LookupOptions,
ClientRevision);
LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsarLookupSids2(%ws) end (0x%x)\n", LsapDbLookupGetLevel(LookupLevel), Status) );
return Status;
}
NTSTATUS
LsarLookupSids3(
IN handle_t RpcHandle,
IN PLSAPR_SID_ENUM_BUFFER SidEnumBuffer,
OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
IN LSAP_LOOKUP_LEVEL LookupLevel,
IN OUT PULONG MappedCount,
IN ULONG LookupOptions,
IN ULONG ClientRevision
)
/*++
Routine Description:
This routine is the server entry point for the IDL LsarLookupSids3.
It accepts an RPC binding handle, instead of a LSA context handle; otherwise
it behaves identically to LsarLookupSids2
Arguments:
RpcHandle -- an RPC binding handle
Rest -- See LsapLookupSids
Return Values:
See LsapLookupSids
--*/
{
NTSTATUS Status;
LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsarLookupSids3(%ws) start\n", LsapDbLookupGetLevel(LookupLevel)) );
Status = LsapLookupSids(NULL,
SidEnumBuffer,
ReferencedDomains,
TranslatedNames,
LookupLevel,
MappedCount,
LookupOptions,
ClientRevision);
LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsarLookupSids3(%ws) end (0x%x)\n", LsapDbLookupGetLevel(LookupLevel), Status) );
return Status;
}
NTSTATUS
LsapLookupSids(
IN LSAPR_HANDLE PolicyHandle,
IN PLSAPR_SID_ENUM_BUFFER SidEnumBuffer,
OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
IN LSAP_LOOKUP_LEVEL LookupLevel,
IN OUT PULONG MappedCount,
IN ULONG LookupOptions,
IN ULONG ClientRevision
)
/*++
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.
LookupOptions - flags to control the lookup. Currently non defined
ClientRevision -- the version of the client
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, TempStatus;
PLSAPR_SID *Sids = (PLSAPR_SID *) SidEnumBuffer->SidInfo;
ULONG Count = SidEnumBuffer->Entries;
BOOLEAN PolicyHandleReferencedHere = FALSE;
PPOLICY_DNS_DOMAIN_INFO PolicyDnsDomainInfo = NULL;
PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
LSA_HANDLE ControllerPolicyHandle = NULL;
ULONG DomainIndex;
ULONG SidIndex;
LSAPR_TRUST_INFORMATION TrustInformation;
PLSAPR_TRANSLATED_NAME_EX OutputNames = NULL;
ULONG OutputNamesLength;
PLSAPR_TRUST_INFORMATION Domains = NULL;
ULONG CompletelyUnmappedCount = Count;
ULONG LocalDomainsToSearch = 0;
BOOLEAN AlreadyTranslated = FALSE;
LUID LogonId;
ULONG DomainLookupScope;
ULONG PreviousMappedCount;
//
// Set to FALSE when the client is less than nt5
//
BOOLEAN fDoExtendedLookups = TRUE;
LsarpReturnCheckSetup();
LsapTraceEvent(EVENT_TRACE_TYPE_START, LsaTraceEvent_LookupSids);
SecondaryStatus = STATUS_SUCCESS;
//
// Parameter checks
//
if ( NULL == Sids ) {
Status = STATUS_INVALID_PARAMETER;
goto LookupSidsError;
}
//
// Perform an access check
//
Status = LsapDbLookupAccessCheck( PolicyHandle );
if (!NT_SUCCESS(Status)) {
goto LookupSidsError;
}
//
// Determine what scope of resolution to use
//
DomainLookupScope = LsapGetDomainLookupScope(LookupLevel,
ClientRevision);
//
// Init out parameters
//
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;
}
ASSERT( (LookupLevel == LsapLookupWksta)
|| (LookupLevel == LsapLookupPDC)
|| (LookupLevel == LsapLookupTDL)
|| (LookupLevel == LsapLookupGC)
|| (LookupLevel == LsapLookupXForestReferral)
|| (LookupLevel == LsapLookupXForestResolve) );
//
// Access and parameter checks are done -- fork off if this is a
// referral
//
if (LookupLevel == LsapLookupXForestReferral) {
NTSTATUS Status2;
BOOLEAN fAllocateAllNodes = FALSE;
*MappedCount = 0;
Status = LsapDbLookupSidsInTrustedForestsWorker(Count,
Sids,
ReferencedDomains,
TranslatedNames,
&fAllocateAllNodes,
MappedCount,
&SecondaryStatus);
if (fAllocateAllNodes) {
//
// Reallocate the memory in a form the server can return to RPC
//
Status2 = LsapLookupReallocateTranslations((PLSA_REFERENCED_DOMAIN_LIST*)ReferencedDomains,
Count,
(PLSA_TRANSLATED_NAME_EX*)&TranslatedNames->Names,
NULL);
if (!NT_SUCCESS(Status2)) {
//
// This is a fatal resource error - free the memory that
// was returned to us by the chaining call
//
if (*ReferencedDomains) {
midl_user_free(*ReferencedDomains);
*ReferencedDomains = NULL;
}
if (TranslatedNames->Names) {
midl_user_free(TranslatedNames->Names);
TranslatedNames->Names = NULL;
TranslatedNames->Entries = 0;
}
Status = Status2;
}
}
//
// There is nothing more to do
//
goto LookupSidsFinish;
}
//
// 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_EX);
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;
OutputNames[SidIndex].Flags = 0;
}
//
// 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] )) {
ULONG i;
LSAP_WELL_KNOWN_SID_INDEX ShortcutIndex = LsapDummyLastSidIndex;
LsapFreeLsaHeap( TokenUserInformation );
//
// Got a match. Get the username and domain information
// from the LogonId
//
LogonSession = LsapLocateLogonSession ( &LogonId );
//
// During setup, we may get NULL returned for the logon session.
//
if (LogonSession == NULL) {
goto NormalLookupPath;
}
UserSid = LogonSession->UserSid;
for (i = 0; i < NELEMENTS(LsapShortcutLookupList); i++ ) {
if (RtlEqualLuid(&LogonId, &LsapShortcutLookupList[i].LogonId)) {
ShortcutIndex = LsapShortcutLookupList[i].LookupIndex;
}
}
if (ShortcutIndex != LsapDummyLastSidIndex) {
AccountName = LsapDbWellKnownSidName( ShortcutIndex );
AuthorityName = LsapDbWellKnownSidDescription( ShortcutIndex );
} else {
AccountName = &LogonSession->AccountName;
AuthorityName = &LogonSession->AuthorityName;
}
//
// N.B. To maintain app compatibility, return SidTypeUser for
// the case of a single SID being looked up that is also the
// SID of the caller. See bug 90589
//
OutputNames[0].Use = SidTypeUser;
//
// DomainSid will be allocated for us, free with MIDL_user_free
//
Status = LsapSplitSid( UserSid, &DomainSid, &Rid );
if ( !NT_SUCCESS(Status)) {
LsapReleaseLogonSession( LogonSession );
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].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.
//
LsapReleaseLogonSession( LogonSession );
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
);
//
// DomainSid has been copied, free it now
//
MIDL_user_free( DomainSid );
if (!NT_SUCCESS(Status)) {
goto NormalLookupPath;
}
ASSERT( OutputNames[0].DomainIndex == 0 );
*MappedCount = 1;
LsapTraceEvent(EVENT_TRACE_TYPE_END, LsaTraceEvent_LookupSids);
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.
//
if ( LookupLevel != LsapLookupGC ) {
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;
}
}
ASSERT( (LookupLevel == LsapLookupWksta)
|| (LookupLevel == LsapLookupPDC)
|| (LookupLevel == LsapLookupTDL)
|| (LookupLevel == LsapLookupXForestResolve) );
//
// 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) {
ULONG MappedByCache = 0;
//
// This boolean indicates whether a post nt4 server
// processed our remote lookups. This will be set
// to TRUE when the current machine is part of a domain and
// the secure channel is to a post nt4 DC
//
BOOLEAN fDownlevelSecureChannel = FALSE;
MappedByCache = *MappedCount;
//
// Try the cache first
//
Status = LsapDbMapCachedSids(
Sids,
Count,
FALSE, // don't use old entries
*ReferencedDomains,
TranslatedNames,
MappedCount
);
if (!NT_SUCCESS(Status)) {
goto LookupSidsError;
}
//
// Note: we must update the CompletelyUnmappedCount here since
// we may have potentially incremented the MappedCount
//
MappedByCache = *MappedCount - MappedByCache;
CompletelyUnmappedCount -= MappedByCache;
if (*MappedCount == Count) {
goto LookupSidsFinish;
}
Status = LsapDbLookupGetDomainInfo(NULL,
&PolicyDnsDomainInfo);
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 (PolicyDnsDomainInfo->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,
&PolicyDnsDomainInfo->Name,
sizeof( UNICODE_STRING)
);
TrustInformation.Sid = (PSID) PolicyDnsDomainInfo->Sid;
Status = LsapDbLookupSidsInPrimaryDomain(
Count,
Sids,
&TrustInformation,
*ReferencedDomains,
TranslatedNames,
LsapLookupPDC,
MappedCount,
&CompletelyUnmappedCount,
&TempStatus,
&fDownlevelSecureChannel
);
if (!NT_SUCCESS(Status)) {
goto LookupSidsError;
}
if (TempStatus == STATUS_TRUSTED_RELATIONSHIP_FAILURE) {
MappedByCache = *MappedCount;
Status = LsapDbMapCachedSids(
Sids,
Count,
TRUE, // Use old entries
*ReferencedDomains,
TranslatedNames,
MappedCount
);
if (!NT_SUCCESS(Status)) {
goto LookupSidsError;
}
MappedByCache = *MappedCount - MappedByCache;
CompletelyUnmappedCount -= MappedByCache;
}
if ( !NT_SUCCESS( TempStatus ) && NT_SUCCESS( SecondaryStatus ) ) {
SecondaryStatus = TempStatus;
}
if (*MappedCount == Count) {
goto LookupSidsFinish;
}
//
// Now, search by sid history if we are a member of an DS aware
// domain and our secure channel DC is not DS aware
//
if ( fDownlevelSecureChannel
&& (PolicyDnsDomainInfo->DnsDomainName.Length > 0) ) {
Status = LsapDbLookupSidsInGlobalCatalogWks(
Count,
Sids,
(PLSAPR_REFERENCED_DOMAIN_LIST) *ReferencedDomains,
TranslatedNames,
MappedCount,
&CompletelyUnmappedCount,
&TempStatus
);
if (!NT_SUCCESS(Status)) {
goto LookupSidsError;
}
if ( !NT_SUCCESS( TempStatus ) && NT_SUCCESS( SecondaryStatus ) ) {
SecondaryStatus = TempStatus;
}
}
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;
}
ASSERT( (LookupLevel == LsapLookupWksta)
|| (LookupLevel == LsapLookupPDC)
|| (LookupLevel == LsapLookupGC)
|| (LookupLevel == LsapLookupXForestResolve) );
//
// 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.
//
if (LsapProductType != NtProductLanManNt) {
Status = STATUS_DOMAIN_CTRLR_CONFIG_ERROR;
goto LookupSidsError;
}
if (DomainLookupScope & LSAP_LOOKUP_RESOLVE_ISOLATED_DOMAINS) {
//
// Lookup the items as domain SID's
//
PreviousMappedCount = *MappedCount;
Status = LsapDbLookupSidsAsDomainSids(DomainLookupScope,
Count,
Sids,
(PLSAPR_REFERENCED_DOMAIN_LIST) *ReferencedDomains,
TranslatedNames,
MappedCount);
if (!NT_SUCCESS(Status)) {
goto LookupSidsError;
}
CompletelyUnmappedCount -= (*MappedCount - PreviousMappedCount);
}
if (DomainLookupScope & LSAP_LOOKUP_TRUSTED_DOMAIN_TRANSITIVE) {
//
// Next, check the global catalog for sids that belong to post nt4 domains
//
Status = LsapDbLookupSidsInGlobalCatalog(
Count,
Sids,
(PLSAPR_REFERENCED_DOMAIN_LIST) *ReferencedDomains,
TranslatedNames,
MappedCount,
&CompletelyUnmappedCount,
TRUE,
&TempStatus
);
if (!NT_SUCCESS(Status)) {
goto LookupSidsError;
}
if ( !NT_SUCCESS( TempStatus ) && NT_SUCCESS( SecondaryStatus ) ) {
SecondaryStatus = TempStatus;
}
}
if (DomainLookupScope & LSAP_LOOKUP_TRUSTED_FOREST) {
ASSERT( (LookupLevel == LsapLookupWksta)
|| (LookupLevel == LsapLookupPDC)
|| (LookupLevel == LsapLookupGC));
//
// Next check for trusted forest SID's
//
Status = LsapDbLookupSidsInTrustedForests(
Count,
Sids,
(PLSAPR_REFERENCED_DOMAIN_LIST) *ReferencedDomains,
TranslatedNames,
MappedCount,
&CompletelyUnmappedCount,
&TempStatus
);
if (!NT_SUCCESS(Status)) {
goto LookupSidsError;
}
if ( !NT_SUCCESS( TempStatus ) && NT_SUCCESS( SecondaryStatus ) ) {
SecondaryStatus = TempStatus;
}
}
if (DomainLookupScope & LSAP_LOOKUP_TRUSTED_DOMAIN_DIRECT) {
ASSERT((LookupLevel == LsapLookupWksta)
|| (LookupLevel == LsapLookupPDC));
//
// Obtain the Trusted Domain List and search all Trusted Domains
// except ourselves.
//
Status = LsapDbLookupSidsInTrustedDomains(
Count,
Sids,
!(DomainLookupScope & LSAP_LOOKUP_TRUSTED_DOMAIN_TRANSITIVE),
// if we didn't go the GC, then
// include intraforest trusts
(PLSAPR_REFERENCED_DOMAIN_LIST) *ReferencedDomains,
TranslatedNames,
LsapLookupTDL,
MappedCount,
&CompletelyUnmappedCount,
&TempStatus
);
if (!NT_SUCCESS(Status)) {
goto LookupSidsError;
}
if ( !NT_SUCCESS( TempStatus ) && NT_SUCCESS( SecondaryStatus ) ) {
SecondaryStatus = TempStatus;
}
}
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 ((LookupLevel == LsapLookupWksta) &&
(CompletelyUnmappedCount != 0) &&
(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 no sids could be mapped it is likely due to the
// secondary status
//
if ( (STATUS_NONE_MAPPED == Status)
&& (STATUS_NONE_MAPPED != SecondaryStatus)
&& LsapRevisionCanHandleNewErrorCodes( ClientRevision )
&& !NT_SUCCESS( SecondaryStatus ) ) {
Status = SecondaryStatus;
goto LookupSidsError;
}
LsapTraceEvent(EVENT_TRACE_TYPE_END, LsaTraceEvent_LookupSids);
LsarpReturnPrologue();
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)->Domains );
}
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 = STATUS_SUCCESS;
LSAP_DB_ENUMERATION_ELEMENT LastElement;
PLSAP_DB_ENUMERATION_ELEMENT FirstElement, NextElement = NULL, 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)) {
//
// If preferred length was zero, NextElement may be NULL
//
if (NextElement != NULL) {
FirstElement = NextElement->Next;
MIDL_user_free( NextElement->Sid );
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;
}
EntriesRead = 0;
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 {
if ( NT_SUCCESS( Status )) {
ASSERT( FALSE );
Status = STATUS_INTERNAL_ERROR;
}
goto FindNextError;
}
if ( SidKeyValueLength == 0 ) {
ASSERT( FALSE );
Status = STATUS_INTERNAL_ERROR;
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;
}
if ( SidKeyValueLength == 0 ) {
ASSERT( FALSE );
Status = STATUS_INTERNAL_ERROR;
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_EX 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 = STATUS_SUCCESS;
ULONG SidNumber;
ULONG UnmappedSidsRemaining;
PLSAPR_TRANSLATED_NAME_EX OutputNames = NULL;
ULONG PrefixSidLength;
UCHAR SubAuthorityCount;
LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex;
LSAPR_TRUST_INFORMATION TrustInformation;
PLSAPR_SID Sid = NULL;
PLSAPR_SID PrefixSid = NULL;
OutputNames = TranslatedNames->Names;
UnmappedSidsRemaining = Count;
//
// 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.
//
if (LsapDbLookupIndexWellKnownSid( Sid, &WellKnownSidIndex ) &&
!SID_IS_RESOLVED_BY_SAM(WellKnownSidIndex)) {
//
// 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(
NULL,
(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;
}
//
// Return output parameters.
//
*MappedCount = Count - UnmappedSidsRemaining;
*CompletelyUnmappedCount = UnmappedSidsRemaining;
return(Status);
LookupIsolatedWellKnownSidsError:
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 = LsapNullSidIndex; 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 can
// theoretically be 8 bits which requires 3 digits
//
TextSidSize = sizeof("S-nnn-");
//
// 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 = NULL;
ANSI_STRING SidAnsi;
if (AllocateDestinationString) {
SidU->Buffer = NULL;
}
//
// 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) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// Convert the Sid to ASCIIZ and place in the buffer.
//
Status = LsapDbSidToTextSid(Sid, TextSid);
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
//
// 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 );
if ( SidU->Buffer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
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);
Cleanup:
if ( TextSid != NULL) {
LsapFreeLsaHeap(TextSid);
}
if (!NT_SUCCESS(Status)) {
if (AllocateDestinationString) {
if ( SidU->Buffer != NULL ) {
MIDL_user_free(SidU->Buffer);
SidU->Buffer = NULL;
}
}
}
return Status;
}
NTSTATUS
LsapDbLookupTranslateUnknownSids(
IN ULONG Count,
IN PLSAPR_SID *Sids,
IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES_EX 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_EX Names = (PLSA_TRANSLATED_NAME_EX) TranslatedNames->Names;
ULONG CleanupFreeListOptions = (ULONG) 0;
UnmappedCount = Count - MappedCount;
//
// 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 = RtlConvertSidToUnicodeString( &NameU, Sid, TRUE );
if (!NT_SUCCESS(Status)) {
goto TranslateUnknownSidsError;
}
}
//
// Copy the Unicode Name to the output, allocating memory for
// its buffer via MIDL_user_allocate
//
Status = LsapRpcCopyUnicodeString(
NULL,
&Names[SidIndex].Name,
&NameU
);
RtlFreeUnicodeString(&NameU);
if (!NT_SUCCESS(Status)) {
goto TranslateUnknownSidsError;
}
//
// Decrement the remaining Unmapped Count
//
UnmappedCount--;
}
TranslateUnknownSidsFinish:
return(Status);
TranslateUnknownSidsError:
goto TranslateUnknownSidsFinish;
}
NTSTATUS
LsapDbLookupSidsInLocalDomains(
IN ULONG Count,
IN PLSAPR_SID *Sids,
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES_EX 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 = STATUS_SUCCESS,
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 = LsapDbLookupGetDomainInfo((PPOLICY_ACCOUNT_DOMAIN_INFO *) &PolicyAccountDomainInfo,
NULL);
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:
//
// 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_EX 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_EX
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 = LSA_UNKNOWN_INDEX,
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));
if ( !NT_SUCCESS( Status ) ) {
return( Status );
}
SamReturnedNames.Count = 0;
SamReturnedNames.Element = NULL;
SamReturnedUses.Count = 0;
SamReturnedUses.Element = NULL;
OutputNames = (PLSA_TRANSLATED_NAME_EX) 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)) {
if ( Status == STATUS_INVALID_SERVER_STATE ) {
Status = SamrLookupIdsInDomain(
LocalSamDomainHandle,
RelativeIdCount,
RelativeIds,
&SamReturnedNames,
&SamReturnedUses
);
}
//
// 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++;
} else {
//
// This sid doesn't exist; if this search is on a domain
// controller, then we must consider that this sid maybe
// part of a sid history, thus we shouldn't map it
//
if ( (LsapProductType == NtProductLanManNt)
&& (LocalDomain == LSAP_DB_SEARCH_ACCOUNT_DOMAIN) ) {
RelativeIdCount--;
}
}
}
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_EX TranslatedNames,
IN LSAP_LOOKUP_LEVEL LookupLevel,
IN OUT PULONG MappedCount,
IN OUT PULONG CompletelyUnmappedCount,
OUT NTSTATUS *NonFatalStatus,
OUT BOOLEAN *fDownlevelSecureChannel
)
/*++
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.
NonFatalStatus - a status to indicate reasons why no sids could have been
resolved
fDownlevelSecureChannel - TRUE if secure channel DC is nt4 or below; FALSE
otherwise
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;
ULONG NextLevelCount;
ULONG NextLevelMappedCount;
ULONG SidIndex;
ULONG NextLevelSidIndex;
PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
PLSA_TRANSLATED_NAME_EX NextLevelNames = NULL;
PLSAPR_SID *NextLevelSids = NULL;
LONG FirstEntryIndex;
PULONG SidIndices = NULL;
BOOLEAN PartialSidTranslationsAttempted = FALSE;
ULONG ServerRevision = 0;
LSAPR_TRUST_INFORMATION_EX TrustInfoEx;
LsapConvertTrustToEx(&TrustInfoEx, TrustInformation);
*NonFatalStatus = STATUS_SUCCESS;
// Assume we don't go to the GC
*fDownlevelSecureChannel = FALSE;
//
// If there are no completely unmapped Sids remaining, just return.
//
if (*CompletelyUnmappedCount == (ULONG) 0) {
goto LookupSidsInPrimaryDomainFinish;
}
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 = LsaDbLookupSidChainRequest(&TrustInfoEx,
NextLevelCount,
(PSID *) NextLevelSids,
(PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
&NextLevelNames,
LookupLevel,
&NextLevelMappedCount,
&ServerRevision
);
//
// 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.
//
//
// Make sure we note the server revision
//
if ( 0 != ServerRevision ) {
if ( ServerRevision & LSA_CLIENT_PRE_NT5 ) {
*fDownlevelSecureChannel = TRUE;
}
}
if (!NT_SUCCESS(Status) && Status != STATUS_NONE_MAPPED) {
//
// Let the caller know there is a trust problem
//
if ( LsapDbIsStatusConnectionFailure(Status) ) {
*NonFatalStatus = Status;
}
Status = STATUS_SUCCESS;
goto LookupSidsInPrimaryDomainFinish;
}
//
// Cache any sids that came back
//
(void) LsapDbUpdateCacheWithSids(
NextLevelSids,
NextLevelCount,
NextLevelReferencedDomains,
NextLevelNames
);
//
// 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]) ) {
SidIndex = SidIndices[NextLevelSidIndex];
if (NextLevelNames[NextLevelSidIndex].Use != SidTypeUnknown) {
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)--;
}
}
//
// Update the Referenced Domain List if a new one was produced
// from the merge. We retain the original top-level structure.
// We do this regardless of whether we succeeded or failed, so
// that we are guarenteed to get it cleaned up.
//
if (OutputReferencedDomains != NULL) {
if (ReferencedDomains->Domains != NULL) {
MIDL_user_free( ReferencedDomains->Domains );
ReferencedDomains->Domains = NULL;
}
*ReferencedDomains = *OutputReferencedDomains;
MIDL_user_free( OutputReferencedDomains );
OutputReferencedDomains = NULL;
}
if (!NT_SUCCESS(Status)) {
goto LookupSidsInPrimaryDomainError;
}
//
// Update the Mapped Count and close the Controller Policy
// Handle.
//
*MappedCount += NextLevelMappedCount;
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;
}
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 BOOLEAN fIncludeIntraforest,
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
IN LSAP_LOOKUP_LEVEL LookupLevel,
IN OUT PULONG MappedCount,
IN OUT PULONG CompletelyUnmappedCount,
OUT NTSTATUS *NonFatalStatus
)
/*++
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.
fIncludeIntraforest -- if TRUE, trusted domains in our local forest
are searched.
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.
NonFatalStatus - a status to indicate reasons why no sids could have been
resolved
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;
*NonFatalStatus = STATUS_SUCCESS;
//
// 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,
fIncludeIntraforest,
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 ( WorkList &&
!NT_SUCCESS( WorkList->NonFatalStatus ) )
{
//
// Propogate the error as non fatal
//
//
*NonFatalStatus = WorkList->NonFatalStatus;
}
//
// 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
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_EX 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 = 0;
//
// 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
LsapDbLookupSidsInGlobalCatalog(
IN ULONG Count,
IN PLSAPR_SID *Sids,
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
IN OUT PULONG MappedCount,
IN OUT PULONG CompletelyUnmappedCount,
IN BOOLEAN fDoSidHistory,
OUT NTSTATUS *NonFatalStatus
)
/*++
Routine Description:
This routine looks the list of sids that have yet to be resolved.
If the any of the sids belong to domain that are stored in the DS,
then therse sids are packages up and sent to a GC for translation.
Note: this will resolve sids from domains that we trust directly and
indirectly
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.
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.
fDoSidHistory - if TRUE then the sids will try to be resolved via sid history
NonFatalStatus - a status to indicate reasons why no sids could have been
resolved
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;
ULONG cGcSids = 0;
PSID *GcSidArray = NULL;
BOOLEAN *PossibleGcSidArray = NULL;
SID_NAME_USE *GcSidNameUse = NULL;
ULONG *GcSidFlags = NULL;
UNICODE_STRING *GcNames = NULL;
ULONG *GcSidOriginalIndex = NULL;
SAMPR_RETURNED_USTRING_ARRAY NameArray;
UNICODE_STRING DomainName, UserName;
UNICODE_STRING BackSlash;
ULONG i;
// Parameter check
ASSERT( Count == TranslatedNames->Entries );
*NonFatalStatus = STATUS_SUCCESS;
if ( !SampUsingDsData() ) {
//
// Only useful if the ds is running
//
return STATUS_SUCCESS;
}
RtlZeroMemory( &NameArray, sizeof(NameArray) );
RtlInitUnicodeString( &BackSlash, L"\\" );
//
// Determine what sids are part of known nt5 domains
// and package into an array
//
PossibleGcSidArray = MIDL_user_allocate( Count * sizeof(BOOLEAN) );
if ( !PossibleGcSidArray ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Finish;
}
RtlZeroMemory( PossibleGcSidArray, Count * sizeof(BOOLEAN) );
for ( i = 0; i < Count; i++ ) {
PSID DomainSid = NULL;
ULONG Rid;
//
// Note: we want names that have just "unknown" set; they could be
// partially mapped, this is fine.
//
if ( (TranslatedNames->Names[i].Use == SidTypeUnknown) ) {
Status = LsapSplitSid( Sids[i],
&DomainSid,
&Rid );
if ( !NT_SUCCESS( Status ) ) {
goto Finish;
}
//
// OPTIMIZE -- if DomainSid is current sid and we don't look up
// by sid history then we shouldn't include the sid here
//
cGcSids++;
PossibleGcSidArray[i] = TRUE;
MIDL_user_free( DomainSid );
}
}
if ( 0 == cGcSids ) {
// nothing to do
goto Finish;
}
//
// Allocate lots of space to hold the resolved sids; this space will
// be freed at the end of the routine
//
GcSidArray = MIDL_user_allocate( cGcSids * sizeof(PSID) );
if ( !GcSidArray ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Finish;
}
RtlZeroMemory( GcSidArray, cGcSids * sizeof(PSID) );
GcSidOriginalIndex = MIDL_user_allocate( cGcSids * sizeof(ULONG) );
if ( !GcSidOriginalIndex ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Finish;
}
RtlZeroMemory( GcSidOriginalIndex, cGcSids * sizeof(ULONG) );
//
// Package up the sids
//
cGcSids = 0;
for ( i = 0; i < Count; i++ ) {
if ( PossibleGcSidArray[i] ) {
GcSidArray[cGcSids] = Sids[i];
GcSidOriginalIndex[cGcSids] = i;
cGcSids++;
}
}
// we are done with this
MIDL_user_free( PossibleGcSidArray );
PossibleGcSidArray = NULL;
GcSidNameUse = MIDL_user_allocate( cGcSids * sizeof(SID_NAME_USE) );
if ( !GcSidNameUse ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Finish;
}
GcSidFlags = MIDL_user_allocate( cGcSids * sizeof(ULONG) );
if ( !GcSidFlags ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Finish;
}
for ( i = 0; i < cGcSids; i++ ) {
GcSidNameUse[i] = SidTypeUnknown;
GcSidFlags[i] = 0;
}
LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: Chaining a SID request to a GC\n"));
//
// Call into SAM to resolve the sids at a GC
//
Status = SamIGCLookupSids( cGcSids,
GcSidArray,
fDoSidHistory ? SAMP_LOOKUP_BY_SID_HISTORY : 0,
GcSidFlags,
GcSidNameUse,
&NameArray );
if (!NT_SUCCESS(Status)) {
LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: Chain to GC request failed (0x%x)\n", Status));
}
if ( STATUS_DS_GC_NOT_AVAILABLE == Status ) {
//
// Ok, don't update the mapped count since no names were
// resolved
//
LsapDbLookupReportEvent0( 1,
EVENTLOG_WARNING_TYPE,
LSAEVENT_LOOKUP_GC_FAILED,
sizeof( ULONG ),
&Status);
*NonFatalStatus = Status;
Status = STATUS_SUCCESS;
goto Finish;
}
// Any other error is fatal
if ( !NT_SUCCESS( Status ) ) {
goto Finish;
}
//
// For each name resolved, put back in the original array and update
// the referenced domain's list
//
for ( i = 0; i < cGcSids; i++ ) {
BOOLEAN fStatus;
ULONG OriginalIndex;
PSID DomainSid = NULL;
LSAPR_TRUST_INFORMATION TrustInformation;
ULONG Rid;
ULONG DomainIndex = LSA_UNKNOWN_INDEX;
USHORT Length;
RtlZeroMemory( &TrustInformation, sizeof(TrustInformation) );
RtlZeroMemory( &DomainName, sizeof(DomainName) );
RtlZeroMemory( &UserName, sizeof(UserName) );
if (GcSidFlags[i] & SAMP_FOUND_XFOREST_REF) {
//
// Flag this entry to be resolved in a trusted forest
//
OriginalIndex = GcSidOriginalIndex[i];
TranslatedNames->Names[OriginalIndex].Flags |= LSA_LOOKUP_SID_XFOREST_REF;
}
if ( SidTypeUnknown == GcSidNameUse[i] ) {
//
// Move on to the next one, right away
//
goto IterationCleanup;
}
//
// This name was resolved! Find the domain reference element
//
if ( GcSidNameUse[i] != SidTypeDomain ) {
//
// Get downlevel domain name and then get sid
//
LsapRtlSplitNames( (UNICODE_STRING*) &NameArray.Element[i],
1,
&BackSlash,
&DomainName,
&UserName );
if ( DomainName.Length > 0 ) {
ASSERT( DomainName.Buffer );
DomainName.Buffer[DomainName.Length/2] = L'\0';
Status = LsapGetDomainSidByNetbiosName( DomainName.Buffer,
&DomainSid );
DomainName.Buffer[DomainName.Length/2] = L'\\';
} else {
Status = STATUS_NO_SUCH_DOMAIN;
}
if ( STATUS_NO_SUCH_DOMAIN == Status ) {
//
// We don't know about this domain, thus we can't resolve
// this name so move on to the next one
// This can occur by either the returned name does not have
// domain embedded in it, or we can't find the domain locally
//
Status = STATUS_SUCCESS;
goto IterationCleanup;
}
if ( !NT_SUCCESS( Status ) ) {
// This is fatal
goto IterationCleanup;
}
} else {
DomainSid = GcSidArray[i];
}
fStatus = LsapDbLookupListReferencedDomains( ReferencedDomains,
DomainSid,
&DomainIndex );
if ( FALSE == fStatus ) {
//
// No entry for this domain -- add it
//
// Set the sid
TrustInformation.Sid = DomainSid;
DomainSid = NULL;
// Allocate and set the name
Status = LsapGetDomainNameBySid( TrustInformation.Sid,
(PUNICODE_STRING) &TrustInformation.Name );
if ( STATUS_NO_SUCH_DOMAIN == Status ) {
//
// We longer know about this domain, though we did
// before we sent the name off to the GC.
// Don't resolve this name, but do continue on with
// the next name
//
Status = STATUS_SUCCESS;
goto IterationCleanup;
}
// Any other error is a resource error
if ( !NT_SUCCESS( Status ) ) {
goto IterationCleanup;
}
//
// Add the entry
//
Status = LsapDbLookupAddListReferencedDomains( ReferencedDomains,
&TrustInformation,
&DomainIndex );
if ( !NT_SUCCESS( Status ) ) {
goto IterationCleanup;
}
}
// We should now have a domain index
ASSERT( LSA_UNKNOWN_INDEX != DomainIndex );
// Set the information in the returned array
OriginalIndex = GcSidOriginalIndex[i];
TranslatedNames->Names[OriginalIndex].Flags = ((GcSidFlags[i] & SAMP_FOUND_BY_SID_HISTORY) ? LSA_LOOKUP_SID_FOUND_BY_HISTORY : 0);
TranslatedNames->Names[OriginalIndex].Use = GcSidNameUse[i];
TranslatedNames->Names[OriginalIndex].DomainIndex = DomainIndex;
// Copy over the name
Length = UserName.MaximumLength;
if ( Length > 0 ) {
TranslatedNames->Names[OriginalIndex].Name.Buffer = MIDL_user_allocate( Length );
if ( !TranslatedNames->Names[OriginalIndex].Name.Buffer ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto IterationCleanup;
}
RtlZeroMemory( TranslatedNames->Names[OriginalIndex].Name.Buffer, Length );
TranslatedNames->Names[OriginalIndex].Name.MaximumLength = UserName.MaximumLength;
TranslatedNames->Names[OriginalIndex].Name.Length = UserName.Length;
RtlCopyMemory( TranslatedNames->Names[OriginalIndex].Name.Buffer, UserName.Buffer, UserName.Length );
}
(*MappedCount) += 1;
(*CompletelyUnmappedCount) -= 1;
IterationCleanup:
if ( TrustInformation.Sid
&& TrustInformation.Sid != GcSidArray[i] ) {
MIDL_user_free( TrustInformation.Sid );
}
if ( TrustInformation.Name.Buffer ) {
MIDL_user_free( TrustInformation.Name.Buffer );
}
if ( DomainSid && DomainSid != GcSidArray[i] ) {
MIDL_user_free( DomainSid );
}
if ( !NT_SUCCESS( Status ) ) {
break;
}
} // iterate over names returned from the GC search
Finish:
// Release any memory SAM allocated for us
SamIFree_SAMPR_RETURNED_USTRING_ARRAY( &NameArray );
if ( GcSidOriginalIndex ) {
MIDL_user_free( GcSidOriginalIndex );
}
if ( PossibleGcSidArray ) {
MIDL_user_free( PossibleGcSidArray );
}
if ( GcSidArray ) {
MIDL_user_free( GcSidArray );
}
if ( GcSidNameUse ) {
MIDL_user_free( GcSidNameUse );
}
if ( GcSidFlags ) {
MIDL_user_free( GcSidFlags );
}
if ( !NT_SUCCESS(Status) ) {
// Any memory we've allocated that hasn't been placed in the
// returned arrays here will get freed at a higher level on error.
// So don't try to free it here
NOTHING;
}
return Status;
}
NTSTATUS
LsapDbLookupSidsInGlobalCatalogWks(
IN ULONG Count,
IN PLSAPR_SID *Sids,
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
IN OUT PULONG MappedCount,
IN OUT PULONG CompletelyUnmappedCount,
OUT NTSTATUS *NonFatalStatus
)
{
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_EX NextLevelNames = NULL;
PLSAPR_SID *NextLevelSids = NULL;
LONG FirstEntryIndex;
PULONG SidIndices = NULL;
BOOLEAN PartialSidTranslationsAttempted = FALSE;
LPWSTR ServerName = NULL;
LPWSTR ServerPrincipalName = NULL;
PVOID ClientContext = NULL;
ULONG ServerRevision = 0;
*NonFatalStatus = STATUS_SUCCESS;
//
// If there are no completely unmapped Sids remaining, just return.
//
if (*CompletelyUnmappedCount == (ULONG) 0) {
goto LookupSidsInPrimaryDomainFinish;
}
//
// Open the Policy object on some GC in the forest.
//
Status = LsapDbOpenPolicyGc( &ControllerPolicyHandle );
if (!NT_SUCCESS(Status)) {
//
// We cannot access the Global Catalog. Suppress the error
// and translate Domain Prefix Sids for Sids belonging to
// the Primary Domain.
//
//
// If we can't open a open a secure channel if a DC call
// this a trust relationship problem
//
*NonFatalStatus = STATUS_DS_GC_NOT_AVAILABLE;
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,
LsapLookupGC,
0,
&NextLevelMappedCount,
&ServerRevision
);
//
// 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)) {
//
// Let the caller know there is a trust problem
//
if ( (STATUS_TRUSTED_DOMAIN_FAILURE == Status)
|| (STATUS_DS_GC_NOT_AVAILABLE == Status) ) {
*NonFatalStatus = Status;
}
Status = STATUS_SUCCESS;
goto LookupSidsInPrimaryDomainFinish;
}
//
// Cache any sids that came back
//
(void) LsapDbUpdateCacheWithSids(
NextLevelSids,
NextLevelCount,
NextLevelReferencedDomains,
NextLevelNames
);
//
// 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]) ) {
SidIndex = SidIndices[NextLevelSidIndex];
if (NextLevelNames[NextLevelSidIndex].Use != SidTypeUnknown) {
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)--;
}
}
//
// Update the Referenced Domain List if a new one was produced
// from the merge. We retain the original top-level structure.
// We do this regardless of whether we succeeded or failed, so
// that we are guarenteed to get it cleaned up.
//
if (OutputReferencedDomains != NULL) {
if (ReferencedDomains->Domains != NULL) {
MIDL_user_free( ReferencedDomains->Domains );
ReferencedDomains->Domains = NULL;
}
*ReferencedDomains = *OutputReferencedDomains;
MIDL_user_free( OutputReferencedDomains );
OutputReferencedDomains = NULL;
}
if (!NT_SUCCESS(Status)) {
goto LookupSidsInPrimaryDomainError;
}
//
// Update the Mapped Count and close the Controller Policy
// Handle.
//
*MappedCount += NextLevelMappedCount;
SecondaryStatus = LsaClose( ControllerPolicyHandle );
ControllerPolicyHandle = NULL;
LookupSidsInPrimaryDomainFinish:
//
// 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
LsapDbLookupSidsInTrustedForests(
IN ULONG Count,
IN PLSAPR_SID *Sids,
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
IN OUT PULONG MappedCount,
IN OUT PULONG CompletelyUnmappedCount,
OUT NTSTATUS *NonFatalStatus
)
/*++
Routine Description:
This routine is called during a LsapLookupPDC lookup. It takes all of the
SID's that have been marked as belonging to cross forest domains and
chains to request to either 1) a DC in the root domain of this forest, or
2) a DC in ex-forest if the local DC is a DC in the root domain.
Arguments:
Count -- the number of entries in Sids
Sids -- the total collection of SIDs for the LsapLookupPDC request
ReferencedDomains -- the domains of Sids
TranslatedNames -- the names and characteristics of Sids
Mapped -- the number of Sids that have been fully mapped
CompletelyUnmappedCount -- the number of Sids whose domain portions haven't
been identified.
NonFatalStatus -- a connectivity problem, if any while chaining the request.
Return Values:
STATUS_SUCCESS, or resource error otherwise
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS NextLevelSecondaryStatus = STATUS_SUCCESS;
ULONG NextLevelCount = 0;
ULONG NextLevelMappedCount;
ULONG SidIndex;
ULONG NextLevelSidIndex;
PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
PLSA_TRANSLATED_NAME_EX NextLevelNames = NULL;
LSA_TRANSLATED_NAMES_EX NextLevelNamesStruct;
PLSAPR_SID *NextLevelSids = NULL;
LONG FirstEntryIndex;
PULONG SidIndices = NULL;
LPWSTR ServerName = NULL;
LPWSTR ServerPrincipalName = NULL;
PVOID ClientContext = NULL;
ULONG ServerRevision = 0;
BOOLEAN *PossibleXForestSids = NULL;
BOOLEAN fAllocateAllNodes = FALSE;
*NonFatalStatus = STATUS_SUCCESS;
//
// If there are no completely unmapped Sids remaining, just return.
//
if (*CompletelyUnmappedCount == (ULONG) 0) {
goto LookupSidsInTrustedForestsFinish;
}
//
// Allocate an array to keep track of which SID's are going to
// be sent off
//
PossibleXForestSids = midl_user_allocate(Count * sizeof(BOOLEAN));
if (NULL == PossibleXForestSids) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto LookupSidsInTrustedForestsError;
}
RtlZeroMemory( PossibleXForestSids, Count * sizeof(BOOLEAN) );
NextLevelCount = 0;
for (SidIndex = 0; SidIndex < Count; SidIndex++) {
if (TranslatedNames->Names[SidIndex].Flags & LSA_LOOKUP_SID_XFOREST_REF) {
ULONG Buffer[SECURITY_MAX_SID_SIZE/sizeof( ULONG ) + 1 ];
PSID DomainSid = (PSID)Buffer;
DWORD Size = sizeof(Buffer);
ASSERT( sizeof( Buffer ) >= SECURITY_MAX_SID_SIZE );
if (GetWindowsAccountDomainSid(Sids[SidIndex], DomainSid, &Size)) {
NTSTATUS Status2;
Status2 = LsapDomainHasDirectExternalTrust(NULL,
DomainSid,
NULL,
NULL);
if (NT_SUCCESS(Status2)) {
//
// Don't send of for xforest resolution, since we
// can do it locally instead
//
continue;
} else if ( Status2 != STATUS_NO_SUCH_DOMAIN ) {
goto LookupSidsInTrustedForestsError;
}
}
PossibleXForestSids[SidIndex] = TRUE;
NextLevelCount++;
}
}
if (NextLevelCount == 0) {
goto LookupSidsInTrustedForestsFinish;
}
//
// 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));
if (SidIndices == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto LookupSidsInTrustedForestsError;
}
//
// Allocate an array for the Sids to be looked up at the Domain
// Controller.
//
NextLevelSids = MIDL_user_allocate( sizeof(PSID) * NextLevelCount );
if (NextLevelSids == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto LookupSidsInTrustedForestsError;
}
//
// 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 (PossibleXForestSids[SidIndex]) {
NextLevelSids[NextLevelSidIndex] = Sids[SidIndex];
SidIndices[NextLevelSidIndex] = SidIndex;
NextLevelSidIndex++;
}
}
NextLevelMappedCount = (ULONG) 0;
NextLevelNamesStruct.Entries = 0;
NextLevelNamesStruct.Names = NULL;
Status = LsapDbLookupSidsInTrustedForestsWorker(NextLevelCount,
(PLSAPR_SID *) NextLevelSids,
(PLSAPR_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
(PLSAPR_TRANSLATED_NAMES_EX)&NextLevelNamesStruct,
&fAllocateAllNodes,
&NextLevelMappedCount,
&NextLevelSecondaryStatus);
NextLevelNames = NextLevelNamesStruct.Names;
if (!NT_SUCCESS(Status)
&& LsapDbIsStatusConnectionFailure(Status)) {
*NonFatalStatus = Status;
Status = STATUS_SUCCESS;
goto LookupSidsInTrustedForestsFinish;
} else if (NT_SUCCESS(Status)
&& !NT_SUCCESS(NextLevelSecondaryStatus)) {
*NonFatalStatus = NextLevelSecondaryStatus;
goto LookupSidsInTrustedForestsFinish;
} else if (!NT_SUCCESS(Status)
&& Status != STATUS_NONE_MAPPED) {
//
// Unhandled error; STATUS_NONE_MAPPED is handled to get
// partially resolved names.
//
goto LookupSidsInTrustedForestsError;
}
ASSERT(NT_SUCCESS(Status) || Status == STATUS_NONE_MAPPED);
Status = STATUS_SUCCESS;
//
// 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 LookupSidsInTrustedForestsError;
}
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]) ) {
SidIndex = SidIndices[NextLevelSidIndex];
if (NextLevelNames[NextLevelSidIndex].Use != SidTypeUnknown) {
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)--;
}
}
//
// Update the Referenced Domain List if a new one was produced
// from the merge. We retain the original top-level structure.
// We do this regardless of whether we succeeded or failed, so
// that we are guarenteed to get it cleaned up.
//
if (OutputReferencedDomains != NULL) {
if (ReferencedDomains->Domains != NULL) {
MIDL_user_free( ReferencedDomains->Domains );
ReferencedDomains->Domains = NULL;
}
*ReferencedDomains = *OutputReferencedDomains;
MIDL_user_free( OutputReferencedDomains );
OutputReferencedDomains = NULL;
}
if (!NT_SUCCESS(Status)) {
goto LookupSidsInTrustedForestsError;
}
//
// Update the Mapped Count and close the Controller Policy
// Handle.
//
*MappedCount += NextLevelMappedCount;
LookupSidsInTrustedForestsFinish:
//
// If necessary, free the Next Level Referenced Domain List.
// Note the structure is not allocate_all_nodes
//
if (NextLevelReferencedDomains != NULL) {
if (!fAllocateAllNodes) {
if (NextLevelReferencedDomains->Domains) {
for (NextLevelSidIndex = 0;
NextLevelSidIndex < NextLevelReferencedDomains->Entries;
NextLevelSidIndex++) {
if (NextLevelReferencedDomains->Domains[NextLevelSidIndex].Name.Buffer) {
MIDL_user_free(NextLevelReferencedDomains->Domains[NextLevelSidIndex].Name.Buffer);
}
if (NextLevelReferencedDomains->Domains[NextLevelSidIndex].Sid) {
MIDL_user_free(NextLevelReferencedDomains->Domains[NextLevelSidIndex].Sid);
}
}
MIDL_user_free(NextLevelReferencedDomains->Domains);
}
}
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 ) {
if (!fAllocateAllNodes) {
for (NextLevelSidIndex = 0;
NextLevelSidIndex < NextLevelCount;
NextLevelSidIndex++) {
if (NextLevelNames[NextLevelSidIndex].Name.Buffer) {
MIDL_user_free(NextLevelNames[NextLevelSidIndex].Name.Buffer);
}
}
}
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 (PossibleXForestSids != NULL) {
MIDL_user_free( PossibleXForestSids );
PossibleXForestSids = NULL;
}
return(Status);
LookupSidsInTrustedForestsError:
goto LookupSidsInTrustedForestsFinish;
}
NTSTATUS
LsapDbLookupSidsInTrustedForestsWorker(
IN ULONG Count,
IN PLSAPR_SID *Sids,
OUT PLSAPR_REFERENCED_DOMAIN_LIST * ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
OUT BOOLEAN* fAllocateAllNodes,
IN OUT PULONG MappedCount,
OUT NTSTATUS *NonFatalStatus
)
/*++
Routine Description:
This routine is called during a LsapLookupPDC lookup or a
LsapLookupXForestReferral. This routine assumes all of Sids belong
to cross forest domains and either resolves them if this DC is in the
root domain, or chains them to a DC in the root domain.
Arguments:
Count -- the number of entries in Sids
Sids -- the SID's belonging to a XForest domain
ReferencedDomains -- the domains of Sids
TranslatedNames -- the names and characteristics of Sids
fAllocateAllNodes -- describes how ReferencedDomains and TranslatesSids are
allocated.
Mapped -- the number of Sids that have been fully mapped
NonFatalStatus -- a connectivity problem, if any while chaining the request.
Return Values:
STATUS_SUCCESS, or resource error otherwise
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;
*NonFatalStatus = STATUS_SUCCESS;
*fAllocateAllNodes = FALSE;
if (!LsapDbDcInRootDomain()) {
//
// We are not at the root domain -- forward request
//
PPOLICY_DNS_DOMAIN_INFO DnsDomainInfo = NULL;
LSAPR_TRUST_INFORMATION_EX TrustInfoEx;
//
// Get our forest name
//
Status = LsapDbLookupGetDomainInfo(NULL,
&DnsDomainInfo);
if (!NT_SUCCESS(Status)) {
goto LookupSidsInTrustedForestFinish;
}
RtlZeroMemory(&TrustInfoEx, sizeof(TrustInfoEx));
TrustInfoEx.DomainName = *((LSAPR_UNICODE_STRING*)&DnsDomainInfo->DnsForestName);
Status = LsaDbLookupSidChainRequest(&TrustInfoEx,
Count,
(PSID*)Sids,
(PLSA_REFERENCED_DOMAIN_LIST *)ReferencedDomains,
(PLSA_TRANSLATED_NAME_EX * )&TranslatedNames->Names,
LsapLookupXForestReferral,
MappedCount,
NULL);
if (TranslatedNames->Names) {
TranslatedNames->Entries = Count;
*fAllocateAllNodes = TRUE;
}
if (!NT_SUCCESS(Status)) {
//
// The attempt to chain failed; record the error
// if it is interesting
//
if (LsapDbIsStatusConnectionFailure(Status)) {
*NonFatalStatus = Status;
}
//
// This should not fail the overall request
//
Status = STATUS_SUCCESS;
}
} else {
//
// Split the names up into different forests and issue a work
// request for each one
//
ULONG i;
ULONG CompletelyUnMapped = Count;
TranslatedNames->Names = MIDL_user_allocate(Count * sizeof(LSA_TRANSLATED_NAME_EX));
if (TranslatedNames->Names == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto LookupSidsInTrustedForestFinish;
}
TranslatedNames->Entries = Count;
//
// 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( TranslatedNames->Names, Count * sizeof(LSA_TRANSLATED_NAME_EX));
for (i = 0; i < Count; i++) {
TranslatedNames->Names[i].Use = SidTypeUnknown;
TranslatedNames->Names[i].DomainIndex = LSA_UNKNOWN_INDEX;
}
//
// Create an empty Referenced Domain List.
//
Status = LsapDbLookupCreateListReferencedDomains( ReferencedDomains, 0 );
if (!NT_SUCCESS(Status)) {
goto LookupSidsInTrustedForestFinish;
}
//
// 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 = LsapDbLookupXForestSidsBuildWorkList(
Count,
Sids,
*ReferencedDomains,
TranslatedNames,
LsapLookupXForestResolve,
MappedCount,
&CompletelyUnMapped,
&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 LookupSidsInTrustedForestFinish;
}
//
// Start the work, by dispatching one or more worker threads
// if necessary.
//
Status = LsapDbLookupDispatchWorkerThreads( WorkList );
if (!NT_SUCCESS(Status)) {
goto LookupSidsInTrustedForestFinish;
}
//
// Wait for completion/termination of all items on the Work List.
//
Status = LsapDbLookupAwaitCompletionWorkList( WorkList );
if (!NT_SUCCESS(Status)) {
goto LookupSidsInTrustedForestFinish;
}
if ( !NT_SUCCESS(WorkList->NonFatalStatus) ) {
//
// Propogate the error as non fatal
//
*NonFatalStatus = WorkList->NonFatalStatus;
}
}
LookupSidsInTrustedForestFinish:
//
// If a Work List was created, delete it from the Work Queue
//
if (WorkList != NULL) {
Status = LsapDbLookupDeleteWorkList( WorkList );
WorkList = NULL;
}
return Status;
}
NTSTATUS
LsapDbLookupSidsAsDomainSids(
IN ULONG Flags,
IN ULONG Count,
IN PLSAPR_SID *Sids,
IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
IN OUT PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
IN OUT PULONG MappedCount
)
/*++
Routine Description:
This routine tries to match entries in Sids to domain Sids of
trusted domains.
There are three kinds of trusted domains:
1) domains we directly trusts (both in and out of forest). The LSA TDL
is used for this.
2) domains we trust transitively. The DS cross-ref is used for this.
3) domains we trust via a forest trust. The LSA TDL is used
for this.
Arguments:
Flags -- LSAP_LOOKUP_TRUSTED_DOMAIN_DIRECT
LSAP_LOOKUP_TRUSTED_DOMAIN_TRANSITIVE
LSAP_LOOKUP_TRUSTED_DOMAIN_FOREST_NAMES
Count -- the number of entries in Sids
Sids -- the SID's belonging to a XForest domain
ReferencedDomains -- the domains of Sids
TranslatedNames -- the names and characteristics of Sids
Mapped -- the number of Sids that have been fully mapped
Return Values:
STATUS_SUCCESS, or resource error otherwise
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG SidIndex;
BOOLEAN fTDLLock = FALSE;
LSA_TRUST_INFORMATION TrustInfo;
RtlZeroMemory(&TrustInfo, sizeof(TrustInfo));
for (SidIndex = 0; SidIndex < Count; SidIndex++) {
LSAPR_TRUSTED_DOMAIN_INFORMATION_EX *TrustInfoEx = NULL;
LSAPR_TRUSTED_DOMAIN_INFORMATION_EX TrustInfoBuffer;
PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY TrustEntry = NULL;
PBYTE Buffer[SECURITY_MAX_SID_SIZE];
PSID DomainSid = (PSID)Buffer;
ULONG Length;
ULONG DomainIndex;
BOOLEAN fStatus;
RtlZeroMemory(&TrustInfo, sizeof(TrustInfo));
if (!LsapDbCompletelyUnmappedName(&TranslatedNames->Names[SidIndex])) {
// Already resolved
continue;
}
//
// If this isn't a domain SID, bail
//
Length = sizeof(Buffer);
if (!GetWindowsAccountDomainSid(Sids[SidIndex],
DomainSid,
&Length)) {
continue;
}
if (!EqualSid(DomainSid, Sids[SidIndex])) {
continue;
}
if (Flags & LSAP_LOOKUP_TRUSTED_DOMAIN_TRANSITIVE) {
Status = LsapDomainHasTransitiveTrust(NULL,
Sids[SidIndex],
&TrustInfo);
if (NT_SUCCESS(Status)) {
TrustInfoEx = &TrustInfoBuffer;
RtlZeroMemory(&TrustInfoBuffer, sizeof(TrustInfoBuffer));
TrustInfoEx->FlatName = *(LSAPR_UNICODE_STRING*)&TrustInfo.Name;
TrustInfoEx->Sid = TrustInfo.Sid;
} else if (Status == STATUS_NO_SUCH_DOMAIN) {
Status = STATUS_SUCCESS;
} else {
// This is fatal
goto Exit;
}
}
if ((NULL == TrustInfoEx)
&& (Flags & LSAP_LOOKUP_TRUSTED_DOMAIN_DIRECT)) {
Status = LsapDomainHasDirectTrust(NULL,
Sids[SidIndex],
&fTDLLock,
&TrustEntry);
if (NT_SUCCESS(Status)) {
TrustInfoEx = &TrustEntry->TrustInfoEx;
} else if (Status == STATUS_NO_SUCH_DOMAIN) {
Status = STATUS_SUCCESS;
} else {
// This is fatal
goto Exit;
}
}
if ((NULL == TrustInfoEx)
&& (Flags & LSAP_LOOKUP_TRUSTED_FOREST_ROOT) ) {
Status = LsapDomainHasForestTrust(NULL,
Sids[SidIndex],
&fTDLLock,
&TrustEntry);
if (NT_SUCCESS(Status)) {
TrustInfoEx = &TrustEntry->TrustInfoEx;
} else if (Status == STATUS_NO_SUCH_DOMAIN) {
Status = STATUS_SUCCESS;
} else {
// This is fatal
goto Exit;
}
}
if (TrustInfoEx) {
//
// Match -- add it to the list of resolved SID's
//
fStatus = LsapDbLookupListReferencedDomains( ReferencedDomains,
Sids[SidIndex],
&DomainIndex );
if ( FALSE == fStatus ) {
LSA_TRUST_INFORMATION TempTrustInfo;
//
// No entry for this domain -- add it
//
RtlZeroMemory(&TempTrustInfo, sizeof(TempTrustInfo));
// Set the sid
TempTrustInfo.Sid = TrustInfoEx->Sid;
TempTrustInfo.Name = *(PUNICODE_STRING)&TrustInfoEx->FlatName;
//
// Add the entry
//
Status = LsapDbLookupAddListReferencedDomains( ReferencedDomains,
(PLSAPR_TRUST_INFORMATION) &TempTrustInfo,
&DomainIndex );
if ( !NT_SUCCESS( Status ) ) {
goto Exit;
}
}
// We should now have a domain index
ASSERT( LSA_UNKNOWN_INDEX != DomainIndex );
// Set the information in the returned array
TranslatedNames->Names[SidIndex].Use = SidTypeDomain;
TranslatedNames->Names[SidIndex].DomainIndex = DomainIndex;
RtlZeroMemory( &TranslatedNames->Names[SidIndex].Name, sizeof(UNICODE_STRING) );
//
// Increment the number of items mapped
//
(*MappedCount) += 1;
}
if (fTDLLock) {
LsapDbReleaseLockTrustedDomainList();
fTDLLock = FALSE;
}
if (TrustInfo.Name.Buffer) {
midl_user_free(TrustInfo.Name.Buffer);
TrustInfo.Name.Buffer = NULL;
}
if (TrustInfo.Sid) {
midl_user_free(TrustInfo.Sid);
TrustInfo.Sid = NULL;
}
if (!NT_SUCCESS(Status)) {
goto Exit;
}
}
Exit:
if (fTDLLock) {
LsapDbReleaseLockTrustedDomainList();
fTDLLock = FALSE;
}
if (TrustInfo.Name.Buffer) {
midl_user_free(TrustInfo.Name.Buffer);
TrustInfo.Name.Buffer = NULL;
}
if (TrustInfo.Sid) {
midl_user_free(TrustInfo.Sid);
TrustInfo.Sid = NULL;
}
return Status;
}