/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    dblookup.c

Abstract:

    LSA Database - Lookup Sid and Name routines

    NOTE:  This module should remain as portable code that is independent
           of the implementation of the LSA Database.  As such, it is
           permitted to use only the exported LSA Database interfaces
           contained in db.h and NOT the private implementation
           dependent functions in dbp.h.

Author:

    Scott Birrell       (ScottBi)      November 27, 1992

Environment:

Revision History:

--*/

#include "lsasrvp.h"
#include "ausrvp.h"
#include "dbp.h"

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// Lsa Lookup Sid and Name Private Global State Variables               //         //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

LARGE_INTEGER LsapDbLookupTimeout;
HANDLE LsapDbLookupCompleteEvent = NULL;
HANDLE LsapDbLookupStartedEvent = NULL;



//////////////////////////////////////////////////////////////////////////
//                                                                      //
// Lsa Lookup Sid and Name Routines                                     //
//                                                                      //
//////////////////////////////////////////////////////////////////////////


NTSTATUS
LsarLookupSids(
    IN LSAPR_HANDLE PolicyHandle,
    IN PLSAPR_SID_ENUM_BUFFER SidEnumBuffer,
    OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
    OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
    IN LSAP_LOOKUP_LEVEL LookupLevel,
    IN OUT PULONG MappedCount
    )

/*++

Routine Description:

    This routine is the LSA Server worker routine for the LsaLookupSids
    API.

    The LsaLookupSids API attempts to find names corresponding to Sids.
    If a name can not be mapped to a Sid, the Sid is converted to character
    form.  The caller must have POLICY_LOOKUP_NAMES access to the Policy
    object.

    WARNING:  This routine allocates memory for its output.  The caller is
    responsible for freeing this memory after use.  See description of the
    Names parameter.

Arguments:

    PolicyHandle -  Handle from an LsaOpenPolicy call.

    SidEnumBuffer - Pointer to an enumeration buffer containing a count
        and a pointer to an array of Count pointers to Sids to be mapped
        to names.  The Sids may be well_known SIDs, SIDs of User accounts
        Group Accounts, Alias accounts, or Domains.

    ReferencedDomains - Receives a pointer to a structure describing the
        domains used for the translation.  The entries in this structure
        are referenced by the structure returned via the Names parameter.
        Unlike the Names parameter, which contains an array entry
        for (each translated name, this strutcure will only contain
        component for each domain utilized in the translation.

        When this information is no longer needed, it must be released
        by passing the returned pointer to LsaFreeMemory().

    TranslatedNames - Pointer to a structure which will reference an array
        records describing each translated name.  The nth entry in this array
        provides a translation for the nth entry in the Sids parameter.

        All of the returned names will be isolated names or NULL strings
        (domain names are returned as NULL strings).  If the caller needs
        composite names, they can be generated by prepending the
        isolated name with the domain name and a backslash.  For example,
        if (the name Sally is returned, and it is from the domain Manufact,
        then the composite name would be "Manufact" + "\" + "Sally" or
        "Manufact\Sally".

        When this information is no longer needed, it must be released
        by passing the returned pointer to LsaFreeMemory().

        If a Sid is not translatable, then the following will occur:

        1) If the SID's domain is known, then a reference domain record
           will be generated with the domain's name.  In this case, the
           name returned via the Names parameter is a Unicode representation
           of the relative ID of the account, such as "(3cmd14)" or the null
           string, if the Sid is that of a domain.  So, you might end up
           with a resultant name of "Manufact\(314) for the example with
           Sally above, if Sally's relative id is 314.

        2) If not even the SID's domain could be located, then a full
           Unicode representation of the SID is generated and no domain
           record is referenced.  In this case, the returned string might
           be something like: "(S-1-672194-21-314)".

        When this information is no longer needed, it must be released
        by passing the returned pointer to LsaFreeMemory().

    LookupLevel - Specifies the Level of Lookup to be performed on this
        machine.  Values of this field are are follows:

        LsapLookupWksta - First Level Lookup performed on a workstation
            normally configured for Windows-Nt.   The lookup searches the
            Well-Known Sids/Names, and the Built-in Domain and Account Domain
            in the local SAM Database.  If not all Sids or Names are
            identified, performs a "handoff" of a Second level Lookup to the
            LSA running on a Controller for the workstation's Primary Domain
            (if any).

        LsapLookupPDC - Second Level Lookup performed on a Primary Domain
            Controller.  The lookup searches the Account Domain of the
            SAM Database on the controller.  If not all Sids or Names are
            found, the Trusted Domain List (TDL) is obtained from the
            LSA's Policy Database and Third Level lookups are performed
            via "handoff" to each Trusted Domain in the List.

        LsapLookupTDL - Third Level Lookup performed on a controller
            for a Trusted Domain.  The lookup searches the Account Domain of
            the SAM Database on the controller only.

    MappedCount - Pointer to location that contains a count of the Sids
        mapped so far. On exit, this will be updated.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully and all Sids have
            been translated to names.

        STATUS_SOME_NOT_MAPPED - At least one of the Sids provided was
            translated to a Name, but not all Sids could be translated. This
            is a success status.

        STATUS_NONE_MAPPED - None of the Sids provided could be translated
            to names.  This is an error status, but output is returned.  Such
            output includes partial translations of Sids whose domain could
            be identified, together with their Relative Id in Unicode format,
            and character representations of Sids whose domain could not
            be identified.

        STATUS_ACCESS_DENIED - Caller does not have the appropriate access
            to complete the operation.

        STATUS_DOMAIN_CTRLR_CONFIG_ERROR - Target machine not configured
            as a DC when expected.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
            such as memory to complete the call.
--*/

{
    NTSTATUS Status, SecondaryStatus;
    PLSAPR_SID *Sids = (PLSAPR_SID *) SidEnumBuffer->SidInfo;
    ULONG Count = SidEnumBuffer->Entries;
    BOOLEAN PolicyHandleReferencedHere = FALSE;
    PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
    PPOLICY_LSA_SERVER_ROLE PolicyLsaServerRoleInfo = NULL;
    PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
    LSA_HANDLE ControllerPolicyHandle = NULL;
    ULONG DomainIndex;
    ULONG SidIndex;
    LSAPR_TRUST_INFORMATION TrustInformation;
    PLSAPR_TRANSLATED_NAME OutputNames = NULL;
    ULONG OutputNamesLength;
    PLSAPR_TRUST_INFORMATION Domains = NULL;
    ULONG CompletelyUnmappedCount = Count;
    ULONG LocalDomainsToSearch = 0;
    BOOLEAN AlreadyTranslated = FALSE;
    LUID LogonId;



    SecondaryStatus = STATUS_SUCCESS;


    //
    // Acquire the Lsa Database lock.  Verify that the connection handle is
    // valid, is of the expected type and has all of the desired accesses
    // granted.  Reference the handle.
    //

    Status = LsapDbReferenceObject(
                 PolicyHandle,
                 POLICY_LOOKUP_NAMES,
                 PolicyObject,
                 LSAP_DB_ACQUIRE_LOCK
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsError;
    }

    //
    // We can dereference the original PolicyHandle and release the lock on
    // the LSA Database: if we need to access the database again, we'll
    // use the trusted Lsa handle and the appropriate API will take
    // the lock as required.
    //

    Status = LsapDbDereferenceObject(
                 &PolicyHandle,
                 PolicyObject,
                 LSAP_DB_RELEASE_LOCK,
                 (SECURITY_DB_DELTA_TYPE) 0,
                 Status
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsError;
    }


    TranslatedNames->Entries = SidEnumBuffer->Entries;
    TranslatedNames->Names = NULL;
    *ReferencedDomains = NULL;


    //
    // Verify that all of the Sids passed in are syntactically valid.
    //

    for (SidIndex = 0; SidIndex < Count; SidIndex++) {

        if ((Sids[SidIndex] != NULL) && RtlValidSid( (PSID) Sids[SidIndex])) {

            continue;
        }

        Status = STATUS_INVALID_PARAMETER;
        break;
    }

    if (!NT_SUCCESS( Status )) {

        goto LookupSidsError;
    }

    //
    // Allocate Output Names array buffer.  For now don't place its address on
    // the Free List as this buffer contains others that will be placed on
    // that list and the order of freeing is unknown.
    //

    OutputNamesLength = Count * sizeof(LSA_TRANSLATED_NAME);
    OutputNames = MIDL_user_allocate(OutputNamesLength);

    Status = STATUS_INSUFFICIENT_RESOURCES;

    if (OutputNames == NULL) {

        goto LookupSidsError;
    }

    Status = STATUS_SUCCESS;

    TranslatedNames->Entries = SidEnumBuffer->Entries;
    TranslatedNames->Names = OutputNames;


    //
    // Initialize Output Names array, marking Sid Use as unknown and
    // specifying negative DomainIndex.
    //

    RtlZeroMemory( OutputNames, OutputNamesLength);

    for (SidIndex = 0; SidIndex < Count; SidIndex++) {

        OutputNames[SidIndex].Use = SidTypeUnknown;
        OutputNames[SidIndex].DomainIndex = LSA_UNKNOWN_INDEX;
    }

    //
    // Create an empty Referenced Domain List.
    //

    Status = LsapDbLookupCreateListReferencedDomains( ReferencedDomains, 0 );

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsError;
    }

    if ( Count == 1 ) {

        PUNICODE_STRING AccountName;
        PUNICODE_STRING AuthorityName;
        PSID UserSid;
        PSID DomainSid = NULL;
        ULONG Rid;
        PLSAP_LOGON_SESSION LogonSession;
        PTOKEN_USER TokenUserInformation;

        //
        // Let's see if we're trying to look up the currently logged on
        // user.
        //
        //
        // TokenUserInformation from this call must be freed by calling
        // LsapFreeLsaHeap().
        //

        Status = LsapQueryClientInfo(
                     &TokenUserInformation,
                     &LogonId
                     );

        if ( !NT_SUCCESS( Status )) {

            goto NormalLookupPath;
        }

        if ( RtlEqualSid( TokenUserInformation->User.Sid, Sids[0] )) {

            LsapFreeLsaHeap( TokenUserInformation );

            //
            // Got a match.  Get the username and domain information
            // from the LogonId
            //

            LsapAuLock();

            LogonSession = LsapGetLogonSession ( &LogonId, FALSE );

            //
            // During setup, we may get NULL returned for the logon session.
            //

            if (LogonSession == NULL) {

                LsapAuUnlock();
                goto NormalLookupPath;
            }

            UserSid       = LogonSession->UserSid;
            AccountName   = LogonSession->AccountName;
            AuthorityName = LogonSession->AuthorityName;

            //
            // DomainSid will be allocated for us, free with MIDL_user_free
            //

            Status = LsapSplitSid( UserSid, &DomainSid, &Rid );

            if ( !NT_SUCCESS(Status)) {
                LsapAuUnlock();
                goto NormalLookupPath;
            }

            RtlCopyMemory(
                &TrustInformation.Name,
                AuthorityName,
                sizeof( UNICODE_STRING )
                );

            TrustInformation.Sid = DomainSid;

            //
            // Fill in the Output Translated Name structure.  Note that the
            // buffers for the SID and Unicode Name must be allocated via
            // MIDL_user_allocate() since they will be freed by the calling
            // RPC server stub routine lsarpc_LsarLookupSids() after marshalling.
            //

            OutputNames[0].Use = SidTypeUser;
            OutputNames[0].DomainIndex = 0;

            Status = LsapRpcCopyUnicodeString(
                         NULL,
                         (PUNICODE_STRING) &OutputNames[0].Name,
                         AccountName
                         );

            //
            // Username, AccountName, and UserSid have all been copied
            // from the LogonSession structure, so we can release the AuLock now.
            //

            LsapAuUnlock();

            if (!NT_SUCCESS(Status)) {

                MIDL_user_free(DomainSid);
                goto LookupSidsError;
            }

            //
            // Make an entry in the list of Referenced Domains.
            //

            Status = LsapDbLookupAddListReferencedDomains(
                         *ReferencedDomains,
                         &TrustInformation,
                         (PLONG) &OutputNames[0].DomainIndex
                         );

            ASSERT( OutputNames[0].DomainIndex == 0 );

            //
            // DomainSid has been copied, free it now
            //

            MIDL_user_free( DomainSid );

            if (!NT_SUCCESS(Status)) {
                goto NormalLookupPath;
            }

            *MappedCount = 1;

            return( STATUS_SUCCESS );

        } else {

            LsapFreeLsaHeap( TokenUserInformation );
        }
    }

NormalLookupPath:

    //
    // The local domains to be searched always include the Accounts
    // domain.  For initial lookup targets only, the BUILT_IN domain is
    // also searched.
    //

    LocalDomainsToSearch = LSAP_DB_SEARCH_ACCOUNT_DOMAIN;

    if (LookupLevel == LsapLookupWksta) {

        LocalDomainsToSearch |= LSAP_DB_SEARCH_BUILT_IN_DOMAIN;

        //
        // This is the lowest Lookup Level, normally targeted at a
        // Workstation but possibly targeted at a DC.  Make a first pass of
        // the array of Sids to identify any well-known Isolated Sids.  These
        // are Well Known Sids that do not belong to a real domain.
        //

        Status = LsapDbLookupIsolatedWellKnownSids(
                     Count,
                     Sids,
                     *ReferencedDomains,
                     TranslatedNames,
                     MappedCount,
                     &CompletelyUnmappedCount
                     );

        if (!NT_SUCCESS(Status)) {

            goto LookupSidsError;
        }

        //
        // If all Sids are now mapped or partially mapped, finish.
        //

        if (CompletelyUnmappedCount == (ULONG) 0) {
            goto LookupSidsFinish;
        }
    }

    //
    // There are some remaining completely unmapped Sids.  They may belong to
    // a local SAM Domain.  Currently, there are two such domains, the
    // Built-in Domain and the Accounts Domain.  For initial Lookup Level
    // we search both of these domains.  For higher Lookup Levels we search
    // only the Accounts domain.
    //

    Status = LsapDbLookupSidsInLocalDomains(
                 Count,
                 Sids,
                 *ReferencedDomains,
                 TranslatedNames,
                 MappedCount,
                 &CompletelyUnmappedCount,
                 LocalDomainsToSearch
                 );

    if (!NT_SUCCESS(Status)) {
        goto LookupSidsError;
    }

    //
    // If all Sids are now mapped or partially mapped, finish.
    //

    if (CompletelyUnmappedCount == (ULONG) 0) {
        goto LookupSidsFinish;
    }

    //
    // Not all of the Sids have been identified in the local domains(s).
    // The next step in the search depends on the level of this lookup
    // and how we are configured as follows:
    //
    // Lookup Level         Configuration       Lookup search next
    //
    // LsapLookupWksta      Win Nt              Primary Domain
    //                      LanMan Nt           Trusted Domains
    //
    // LsapLookupPDC        Win Nt              error
    //                      LanMan Nt           Trusted Domains
    //
    // LsaLookupTDL         Win Nt              error
    //                      LanMan Nt           none
    //

    if (LookupLevel == LsapLookupWksta) {

        if (LsapProductType != NtProductLanManNt) {

            Status = LsarQueryInformationPolicy(
                         LsapPolicyHandle,
                         PolicyPrimaryDomainInformation,
                         (PLSAPR_POLICY_INFORMATION *) &PolicyPrimaryDomainInfo
                         );

            if (!NT_SUCCESS(Status)) {

                goto LookupSidsError;
            }

            //
            // If there is no Primary Domain as in the case of a WORKGROUP,
            // just finish up.  Set a default result code STATUS_SUCCESS.  This
            // will be translated to STATUS_SOME_NOT_MAPPED or STATUS_NONE_MAPPED
            // as appropriate.
            //

            Status = STATUS_SUCCESS;

            if (PolicyPrimaryDomainInfo->Sid == NULL) {
                goto LookupSidsFinish;
            }

            //
            // There is a Primary Domain.  Search it for Sids.  Since a
            // Primary Domain is also a Trusted Domain, we use the
            // Trusted Domain search routine.  This routine will "hand off"
            // the search to a Domain Controller's LSA.
            //

            RtlCopyMemory(
                &TrustInformation.Name,
                &PolicyPrimaryDomainInfo->Name,
                sizeof( UNICODE_STRING)
                );

            TrustInformation.Sid = (PSID) PolicyPrimaryDomainInfo->Sid;

            Status = LsapDbLookupSidsInPrimaryDomain(
                         Count,
                         Sids,
                         &TrustInformation,
                         *ReferencedDomains,
                         TranslatedNames,
                         LsapLookupPDC,
                         MappedCount,
                         &CompletelyUnmappedCount
                         );

            if (!NT_SUCCESS(Status)) {

                goto LookupSidsError;
            }

            goto LookupSidsFinish;
        }
    }

    //
    // We reach here in two cases:
    //
    // * Initial Level lookups targeted at DC's
    // * Higher Level Lookups (must be targeted at DC's)
    //
    // For the highest level lookup, that on an individual TDC, there
    // is no more searching to do, since we have already searched the
    // Accounts Domain and we do not follow trust relationships on DC's
    // beyond one level.
    //

    if (LookupLevel == LsapLookupTDL) {

        goto LookupSidsFinish;
    }

    //
    // We are either the initial target of the lookup but not configured
    // as a workstation, or we are the target of a Primary Domain
    // level lookup.  In either case, we must be configured as a DC.
    //

    Status = STATUS_DOMAIN_CTRLR_CONFIG_ERROR;

    if (LsapProductType != NtProductLanManNt) {

        goto LookupSidsError;
    }

    //
    // Obtain the Trusted Domain List and search all Trusted Domains
    // except ourselves.
    //

    Status = LsapDbLookupSidsInTrustedDomains(
                 Count,
                 Sids,
                 (PLSAPR_REFERENCED_DOMAIN_LIST) *ReferencedDomains,
                 TranslatedNames,
                 LsapLookupTDL,
                 MappedCount,
                 &CompletelyUnmappedCount
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsError;
    }

LookupSidsFinish:

    //
    // If there are any unknown Sids (including partially mapped Sids)
    // we need to translate them to character form.  We do this translation
    // at the lowest lookup level in all non-error cases and also in the
    // error case where none were mapped.
    //

    if (NT_SUCCESS(Status)) {

        if ((*MappedCount < Count) &&
            (LookupLevel == LsapLookupWksta) &&
            (AlreadyTranslated == FALSE)) {

            AlreadyTranslated = TRUE;

            //
            // The remaining unmapped Sids are unknown.  They are either
            // completely unmapped, i.e. their domain is unknown, or
            // partially unmapped, their domain being known but their Rid
            // not being recognized. For completely unmapped Sids, translate
            // the entire Sid to character form.  For partially unmapped
            // Sids, translate the Relative Id only to character form.
            //

            Status = LsapDbLookupTranslateUnknownSids(
                         Count,
                         Sids,
                         *ReferencedDomains,
                         TranslatedNames,
                         *MappedCount
                         );

            if (!NT_SUCCESS(Status)) {

                goto LookupSidsError;
            }
        }
    }

    //
    // If some but not all Sids were mapped, return informational status
    // STATUS_SOME_NOT_MAPPED.  If no Sids were mapped, return error
    // STATUS_NONE_MAPPED.
    //

    if (NT_SUCCESS(Status)) {

        if (*MappedCount < Count) {

            Status = STATUS_SOME_NOT_MAPPED;

            if (*MappedCount == 0) {

                Status = STATUS_NONE_MAPPED;
            }
        }
    }

    //
    // If necessary, free the Policy Primary Domain Info
    //

    if (PolicyPrimaryDomainInfo != NULL) {

        LsaIFree_LSAPR_POLICY_INFORMATION(
            PolicyPrimaryDomainInformation,
            (PLSAPR_POLICY_INFORMATION) PolicyPrimaryDomainInfo
            );

        PolicyPrimaryDomainInfo = NULL;
    }

//
// Temporary debug aid.  Trying to find a recurring problem on MIPS
// free builds on printlab6 running spool stress.  This should access
// violate if the problem has been encountered.
//
{   // Temporary debug
    ULONG TmpDiag;
    if (TranslatedNames->Names != NULL) {
        TmpDiag = (TranslatedNames->Names[0].DomainIndex);
    }
}   // end temporary debug

    return(Status);

LookupSidsError:

    //
    // If the LookupLevel is the lowest (Workstation Level, free up
    // the Names and Referenced Domains arrays.
    //

    if (LookupLevel == LsapLookupWksta) {

        //
        // If necessary, free the Names array.
        //

        if (TranslatedNames->Names != NULL) {

            OutputNames = TranslatedNames->Names;

            for (SidIndex = 0; SidIndex < Count; SidIndex++ ) {

                if (OutputNames[SidIndex].Name.Buffer != NULL) {

                    MIDL_user_free( OutputNames[SidIndex].Name.Buffer );
                    OutputNames[SidIndex].Name.Buffer = NULL;
                }
            }

            MIDL_user_free( TranslatedNames->Names );
            TranslatedNames->Names = NULL;
        }

        //
        // If necessary, free the Referenced Domain List.
        //

        if (*ReferencedDomains != NULL) {

            Domains = (*ReferencedDomains)->Domains;

            if (Domains != NULL) {

                for (DomainIndex = 0;
                     DomainIndex < (*ReferencedDomains)->Entries;
                     DomainIndex++) {

                    if (Domains[ DomainIndex ].Name.Buffer != NULL) {

                        MIDL_user_free( Domains[ DomainIndex ].Name.Buffer );
                        Domains[ DomainIndex ].Name.Buffer == NULL;
                    }

                    if (Domains[ DomainIndex ].Sid != NULL) {

                        MIDL_user_free( Domains[ DomainIndex ].Sid );
                        Domains[ DomainIndex ].Sid == NULL;
                    }
                }
            }

            MIDL_user_free( *ReferencedDomains );
            *ReferencedDomains = NULL;
        }
    }

    //
    // If the primary status was a success code, but the secondary
    // status was an error, propagate the secondary status.
    //

    if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {

        Status = SecondaryStatus;
    }

    goto LookupSidsFinish;
}


NTSTATUS
LsapDbEnumerateSids(
    IN LSAPR_HANDLE ContainerHandle,
    IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
    IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
    OUT PLSAP_DB_SID_ENUMERATION_BUFFER DbEnumerationBuffer,
    IN ULONG PreferedMaximumLength
    )

/*++

Routine Description:

    This function enumerates Sids of objects of a given type within a container
    object.  Since there may be more information than can be returned in a
    single call of the routine, multiple calls can be made to get all of the
    information.  To support this feature, the caller is provided with a
    handle that can be used across calls.  On the initial call,
    EnumerationContext should point to a variable that has been initialized
    to 0.

Arguments:

    ContainerHandle -  Handle to a container object.

    ObjectTypeId - Type of object to be enumerated.  The type must be one
        for which all objects have Sids.

    EnumerationContext - API-specific handle to allow multiple calls
        (see Routine Description above).

    DbEnumerationBuffer - Receives a pointer to a structure that will receive
        the count of entries returned in an enumeration information array, and
        a pointer to the array.  Currently, the only information returned is
        the object Sids.  These Sids may be used together with object type to
        open the objects and obtain any further information available.

    PreferedMaximumLength - prefered maximum length of returned data (in 8-bit
        bytes).  This is not a hard upper limit, but serves as a guide.  Due to
        data conversion between systems with different natural data sizes, the
        actual amount of data returned may be greater than this value.

    CountReturned - Pointer to variable which will receive a count of the
        entries returned.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_ACCESS_DENIED - Caller does not have the appropriate access
            to complete the operation.

        STATUS_NO_MORE_ENTRIES - There are no more entries.  This warning
            is returned if no objects have been enumerated because the
            EnumerationContext value passed in is too high.
--*/

{
    NTSTATUS Status;
    LSAP_DB_ENUMERATION_ELEMENT LastElement;
    PLSAP_DB_ENUMERATION_ELEMENT FirstElement, NextElement, FreeElement;
    ULONG DataLengthUsed;
    ULONG ThisBufferLength;
    PSID *Sids = NULL;
    BOOLEAN PreferedMaximumReached = FALSE;
    ULONG EntriesRead;
    ULONG Index, EnumerationIndex;
    BOOLEAN TrustedClient = ((LSAP_DB_HANDLE) ContainerHandle)->Trusted;

    LastElement.Next = NULL;
    FirstElement = &LastElement;

    //
    // If no enumeration buffer provided, return an error.
    //


    if ( !ARGUMENT_PRESENT(DbEnumerationBuffer) ||
         !ARGUMENT_PRESENT(EnumerationContext )    ) {

        return(STATUS_INVALID_PARAMETER);
    }


    //
    // Enumerate objects, stopping when the length of data to be returned
    // reaches or exceeds the Prefered Maximum Length, or reaches the
    // absolute maximum allowed for LSA object enumerations.  We allow
    // the last object enumerated to bring the total amount of data to
    // be returned beyond the Prefered Maximum Length, but not beyond the
    // absolute maximum length.
    //

    EnumerationIndex = *EnumerationContext;

    for(DataLengthUsed = 0, EntriesRead = 0;
        DataLengthUsed < PreferedMaximumLength;
        DataLengthUsed += ThisBufferLength, EntriesRead++) {

        //
        // If the absolute maximum length has been exceeded, back off
        // the last object enumerated.
        //

        if ((DataLengthUsed > LSA_MAXIMUM_ENUMERATION_LENGTH) &&
            (!TrustedClient)) {

            FirstElement = NextElement->Next;
            MIDL_user_free(NextElement);
            break;
        }

        //
        // Allocate memory for next enumeration element.  Set the Sid
        // field to NULL for cleanup purposes.
        //

        NextElement = MIDL_user_allocate(sizeof (LSAP_DB_ENUMERATION_ELEMENT));

        if (NextElement == NULL) {

            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        //
        // Find the next object's Sid, and fill in its object information.
        // Note that memory will be allocated via MIDL_user_allocate
        // and must be freed when no longer required.
        //

        Status = LsapDbFindNextSid(
                     ContainerHandle,
                     &EnumerationIndex,
                     ObjectTypeId,
                     (PLSAPR_SID *) &NextElement->Sid
                     );

        //
        // Stop the enumeration if any error or warning occurs.  Note
        // that the warning STATUS_NO_MORE_ENTRIES will be returned when
        // we've gone beyond the last index.
        //

        if (Status != STATUS_SUCCESS) {

            //
            // Since NextElement is not on the list, it will not get
            // freed at the end so we must free it here.
            //

            MIDL_user_free( NextElement );
            break;
        }

        //
        // Get the length of the data allocated for the object's Sid
        //

        ThisBufferLength = RtlLengthSid( NextElement->Sid );

        //
        // Link the object just found to the front of the enumeration list
        //

        NextElement->Next = FirstElement;
        FirstElement = NextElement;
    }

    //
    // If an error other than STATUS_NO_MORE_ENTRIES occurred, return it.
    // If STATUS_NO_MORE_ENTRIES was returned, we have enumerated all of the
    // entries.  In this case, return STATUS_SUCCESS if we enumerated at
    // least one entry, otherwise propagate STATUS_NO_MORE_ENTRIES back to
    // the caller.
    //

    if (!NT_SUCCESS(Status)) {

        if (Status != STATUS_NO_MORE_ENTRIES) {

            goto EnumerateSidsError;
        }

        if (EntriesRead == 0) {

            goto EnumerateSidsError;
        }

        Status = STATUS_SUCCESS;
    }

    //
    // Some entries were read, allocate an information buffer for returning
    // them.
    //

    Sids = (PSID *) MIDL_user_allocate( sizeof (PSID) * EntriesRead );

    if (Sids == NULL) {

        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto EnumerateSidsError;
    }

    //
    // Memory was successfully allocated for the return buffer.
    // Copy in the enumerated Sids.
    //

    for (NextElement = FirstElement, Index = 0;
        NextElement != &LastElement;
        NextElement = NextElement->Next, Index++) {

        ASSERT(Index < EntriesRead);

        Sids[Index] = NextElement->Sid;
    }

EnumerateSidsFinish:

    //
    // Free the enumeration element structures (if any).
    //

    for (NextElement = FirstElement; NextElement != &LastElement;) {

        //
        // If an error has occurred, dispose of memory allocated
        // for any Sids.
        //

        if (!(NT_SUCCESS(Status) || (Status == STATUS_NO_MORE_ENTRIES))) {

            if (NextElement->Sid != NULL) {

                MIDL_user_free(NextElement->Sid);
            }
        }

        //
        // Free the memory allocated for the enumeration element.
        //

        FreeElement = NextElement;
        NextElement = NextElement->Next;

        MIDL_user_free(FreeElement);
    }

    //
    // Fill in return enumeration structure (0 and NULL in error case).
    //

    DbEnumerationBuffer->EntriesRead = EntriesRead;
    DbEnumerationBuffer->Sids = Sids;
    *EnumerationContext = EnumerationIndex;

    return(Status);

EnumerateSidsError:

    //
    // If necessary, free memory allocated for returning the Sids.
    //

    if (Sids != NULL) {

        MIDL_user_free( Sids );
        Sids = NULL;
    }

    goto EnumerateSidsFinish;
}


NTSTATUS
LsapDbFindNextSid(
    IN LSAPR_HANDLE ContainerHandle,
    IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
    IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
    OUT PLSAPR_SID *NextSid
    )

/*++

Routine Description:

    This function finds the next Sid of object of a given type within a
    container object.  The given object type must be one where objects
    have Sids.  The Sids returned can be used on subsequent open calls to
    access the objects.

Arguments:

    ContainerHandle - Handle to container object.

    EnumerationContext - Pointer to a variable containing the index of
        the object to be found.  A zero value indicates that the first
        object is to be found.

    ObjectTypeId - Type of the objects whose Sids are being enumerated.

    NextSid - Receives a pointer to the next Sid found.

Return Value:

    NTSTATUS - Standard Nt Result Code

        STATUS_INVALID_HANDLE - Invalid ContainerHandle specified

        STATUS_NO_MORE_ENTRIES - Warning that no more entries exist.
--*/

{
    NTSTATUS Status, SecondaryStatus;
    ULONG SidKeyValueLength = 0;
    UNICODE_STRING SubKeyNameU;
    UNICODE_STRING SidKeyNameU;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE ContDirKeyHandle = NULL;
    HANDLE SidKeyHandle = NULL;
    PSID ObjectSid = NULL;

    //
    // Zeroise pointers for cleanup routine
    //

    SidKeyNameU.Buffer = NULL;
    SubKeyNameU.Buffer = NULL;

    //
    // Setup object attributes for opening the appropriate Containing
    // Directory.  For example, if we're looking for Account objects,
    // the containing Directory is "Accounts".  The Unicode strings for
    // containing Directories are set up during Lsa Initialization.
    //

    InitializeObjectAttributes(
        &ObjectAttributes,
        &LsapDbContDirs[ObjectTypeId],
        OBJ_CASE_INSENSITIVE,
        ((LSAP_DB_HANDLE) ContainerHandle)->KeyHandle,
        NULL
        );

    Status = RtlpNtOpenKey(
                 &ContDirKeyHandle,
                 KEY_READ,
                 &ObjectAttributes,
                 0
                 );

    if (!NT_SUCCESS(Status)) {

        ContDirKeyHandle = NULL;  // For error processing
        goto FindNextError;
    }

    //
    // Initialize the Unicode String in which the next object's Logical Name
    // will be returned.  The Logical Name of an object equals its Registry
    // Key relative to its Containing Directory, and is also equal to
    // the Relative Id of the object represented in character form as an
    // 8-digit number with leading zeros.
    //
    // NOTE: The size of buffer allocated for the Logical Name must be
    // calculated dynamically when the Registry supports long names, because
    // it is possible that the Logical Name of an object will be equal to a
    // character representation of the full Sid, not just the Relative Id
    // part.
    //

    SubKeyNameU.MaximumLength = (USHORT) LSAP_DB_LOGICAL_NAME_MAX_LENGTH;
    SubKeyNameU.Length = 0;
    SubKeyNameU.Buffer = LsapAllocateLsaHeap(SubKeyNameU.MaximumLength);

    if (SubKeyNameU.Buffer == NULL) {

        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto FindNextError;
    }

    //
    // Now enumerate the next subkey.
    //

    Status = RtlpNtEnumerateSubKey(
                 ContDirKeyHandle,
                 &SubKeyNameU,
                 *EnumerationContext,
                 NULL
                 );

    if (!NT_SUCCESS(Status)) {

        goto FindNextError;
    }

    //
    // Construct a path to the Sid attribute of the object relative to
    // the containing directory.  This path has the form
    //
    // <Object Logical Name>"\Sid"
    //
    // The Logical Name of the object has just been returned by the
    // above call to RtlpNtEnumerateSubKey.
    //

    Status = LsapDbJoinSubPaths(
                 &SubKeyNameU,
                 &LsapDbNames[Sid],
                 &SidKeyNameU
                 );

    if (!NT_SUCCESS(Status)) {

        goto FindNextError;
    }

    //
    // Setup object attributes for opening the Sid attribute
    //

    InitializeObjectAttributes(
        &ObjectAttributes,
        &SidKeyNameU,
        OBJ_CASE_INSENSITIVE,
        ContDirKeyHandle,
        NULL
        );

    //
    // Open the Sid attribute
    //

    Status = RtlpNtOpenKey(
                 &SidKeyHandle,
                 KEY_READ,
                 &ObjectAttributes,
                 0
                 );

    if (!NT_SUCCESS(Status)) {

        SidKeyHandle = NULL;
        goto FindNextError;
    }

    //
    // Now query the size of the buffer required to read the Sid
    // attribute's value.
    //

    SidKeyValueLength = 0;

    Status = RtlpNtQueryValueKey(
                 SidKeyHandle,
                 NULL,
                 NULL,
                 &SidKeyValueLength,
                 NULL
                 );

    //
    // We expect buffer overflow to be returned from a query buffer size
    // call.
    //

    if (Status == STATUS_BUFFER_OVERFLOW) {

        Status = STATUS_SUCCESS;

    } else {

        goto FindNextError;
    }

    //
    // Allocate memory for reading the Sid attribute.
    //

    ObjectSid = MIDL_user_allocate(SidKeyValueLength);

    if (ObjectSid == NULL) {

        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto FindNextError;
    }

    //
    // Supplied buffer is large enough to hold the SubKey's value.
    // Query the value.
    //

    Status = RtlpNtQueryValueKey(
                 SidKeyHandle,
                 NULL,
                 ObjectSid,
                 &SidKeyValueLength,
                 NULL
                 );

    if (!NT_SUCCESS(Status)) {

        goto FindNextError;
    }

    (*EnumerationContext)++;

    //
    // Return the Sid.
    //

    *NextSid = ObjectSid;

FindNextFinish:

    //
    // If necessary, close the Sid key handle
    //

    if (SidKeyHandle != NULL) {

        SecondaryStatus = NtClose(SidKeyHandle);

#if DBG

        if (!NT_SUCCESS(SecondaryStatus)) {

            DbgPrint("LsapDbFindNextSid: NtClose failed 0x%lx\n", Status);
        }

#endif // DBG

    }

    //
    // If necessary, close the containing directory handle
    //

    if (ContDirKeyHandle != NULL) {

        SecondaryStatus = NtClose(ContDirKeyHandle);

#if DBG
        if (!NT_SUCCESS(SecondaryStatus)) {

            DbgPrint(
                "LsapDbFindNextSid: NtClose failed 0x%lx\n",
                Status
                );
        }

#endif // DBG

    }

    //
    // If necessary, free the Unicode String buffer allocated by
    // LsapDbJoinSubPaths for the Registry key name of the Sid attribute
    // relative to the containing directory Registry key.
    //

    if (SidKeyNameU.Buffer != NULL) {

        RtlFreeUnicodeString( &SidKeyNameU );
    }

    //
    // If necessary, free the Unicode String buffer allocated for
    // Registry key name of the object relative to its containing
    // directory.
    //

    if (SubKeyNameU.Buffer != NULL) {

        LsapFreeLsaHeap( SubKeyNameU.Buffer );
    }

    return(Status);

FindNextError:

    //
    // If necessary, free the memory allocated for the object's Sid.
    //

    if (ObjectSid != NULL) {

        MIDL_user_free(ObjectSid);
        *NextSid = NULL;
    }

    goto FindNextFinish;
}


NTSTATUS
LsapDbLookupIsolatedWellKnownSids(
    IN ULONG Count,
    IN PLSAPR_SID *Sids,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount
    )

/*++

Routine Description:

    This function attempts to identify Sids as Isolated Well-Known Sids
    (Well-Known Sids that do not belong to a domain) and translate them to
    names.  Note that Domain Sids for the Well Known domains themselves
    (e.g the Sid of the Built-in Domain) will be identified.

    WARNING:  This function allocates memory for translated names.  The
    caller is responsible for freeing this memory after it is no longer
    required.

Arguments:

    Count - Specifies the count of Sids provided in the array Sids.

    Sids - Pointer to an array of Sids to be examined.

    TranslatedNames - Pointer to structure that will be initialized to
        references an array of Name translations for the Sids.

    ReferencedDomains - Pointer to a structure that will be initialized to
        reference a list of the domains used for the translation.

        The entries in this structure are referenced by the
        structure returned via the Names parameter.  Unlike the Names
        parameter, which contains an array entry for (each translated name,
        this structure will only contain one component for each domain
        utilized in the translation.

        If the specified location contains NULL, a structure will be allocated
        via MIDL_user_allocate.

    MappedCount - Pointer to location that contains on entry, the number
        of Sids in Sids that have been translated so far.  This number
        is updated if any further Sids are translated by this call.

    CompletelyUnmappedCount - Pointer to location containing the
        count of completely unmapped Sids.  A Sid is completely unmapped
        if it is unknown and also its Domain Prefix Sid is not recognized.
        This count is updated on exit, the number of completely unmapped
        Sids whose Domain Prefices are identified by this routine being
        subtracted from the input value.

Return Value:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Sids may remain partially or completely unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
            such as memory to complete the call.

        STATUS_INVALID_PARAMETER - Invalid parameter or parameter combination.
            - *MappedCount > Count

--*/

{
    NTSTATUS Status;
    ULONG SidNumber;
    ULONG UnmappedSidsRemaining;
    PLSAPR_TRANSLATED_NAME OutputNames = NULL;
    ULONG PrefixSidLength;
    UCHAR SubAuthorityCount;
    LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex;
    LSAPR_TRUST_INFORMATION TrustInformation;
    PLSAPR_SID Sid = NULL;
    PLSAPR_SID PrefixSid = NULL;
    LSAP_MM_FREE_LIST FreeList;
    ULONG CleanupFreeListOptions = (ULONG) 0;

    OutputNames = TranslatedNames->Names;

    UnmappedSidsRemaining = Count;

    //
    // Initialize the Free List.  We need potentially one entry per Sid,
    // for its Well Known Name (if any).
    //

    Status = LsapMmCreateFreeList(&FreeList, UnmappedSidsRemaining);

    if (!NT_SUCCESS(Status)) {

        goto LookupIsolatedWellKnownSidsError;
    }

    //
    // Attempt to identify Sids as Well Known Isolated Sids
    //

    for (SidNumber = 0; SidNumber < Count; SidNumber++) {

        Sid = Sids[SidNumber];

        //
        // Attempt to identify the next Sid using the Well Known Sids table,
        // excluding those Sids that are also in the Built In domain.  For
        // those, we drop through to the Built in Domain search.  Note
        // that only one of these, the Administrators alias is currently
        // present in the table.
        //

        if (LsapDbLookupIndexWellKnownSid( Sid, &WellKnownSidIndex ) &&
            (WellKnownSidIndex != LsapAliasAdminsSidIndex)) {

            //
            // Sid is identified.  Copy its Well Known Name field
            // UNICODE_STRING  structure.  Note that not all Well Known
            // Sids have Well Known Names.  For those Sids without a
            // Well Known Name, this UNICODE_STRING structure specifies
            // the NULL string.
            //

            Status = LsapRpcCopyUnicodeString(
                         &FreeList,
                         (PUNICODE_STRING) &(OutputNames[SidNumber].Name),
                         LsapDbWellKnownSidName(WellKnownSidIndex)
                         );

            if (!NT_SUCCESS(Status)) {

                break;
            }

            //
            // Get the Sid's Use.
            //

            OutputNames[SidNumber].Use = LsapDbWellKnownSidNameUse(WellKnownSidIndex);

            PrefixSid = NULL;

            //
            // If the Sid is a Domain Sid, store pointer to
            // it in the Trust Information.
            //

            if (OutputNames[SidNumber].Use == SidTypeDomain) {

                TrustInformation.Sid = Sid;

            } else {

                //
                // The Sid is not a domain Sid.  Construct the
                // Prefix Sid.  This is equal to the original Sid
                // excluding the lowest subauthority (Relative Id).
                //

                SubAuthorityCount = *RtlSubAuthorityCountSid((PSID) Sid);

                PrefixSidLength = RtlLengthRequiredSid(SubAuthorityCount - 1);

                Status = STATUS_INSUFFICIENT_RESOURCES;

                PrefixSid = MIDL_user_allocate( PrefixSidLength );

                if (PrefixSid == NULL) {

                    break;
                }

                Status = STATUS_SUCCESS;

                RtlCopyMemory( PrefixSid, Sid, PrefixSidLength );

                (*RtlSubAuthorityCountSid( (PSID) PrefixSid ))--;

                TrustInformation.Sid = PrefixSid;
            }

            //
            // Lookup this Domain Sid or Prefix Sid in the Referenced Domain
            // List.  If it is already there, return the DomainIndex for the
            // existing entry and free up the memory allocated for the
            // Prefix Sid (if any).
            //

            if (LsapDbLookupListReferencedDomains(
                    ReferencedDomains,
                    TrustInformation.Sid,
                    (PLONG) &(OutputNames[SidNumber].DomainIndex)
                    )) {

                if ((OutputNames[SidNumber].Use == SidTypeDomain) ||
                    (OutputNames[SidNumber].Name.Buffer != NULL)) {

                    UnmappedSidsRemaining--;
                }

                if (PrefixSid != NULL) {

                    MIDL_user_free(PrefixSid);
                    PrefixSid = NULL;
                }

                continue;
            }

            //
            // This Domain or Prefix Sid is not currently on the
            // Referenced Domain List.  Complete a Trust Information
            // entry and add it to the List.  Copy in the Domain Name
            // (Domain Sids) or NULL string.  Note that we use
            // RtlCopyMemory to copy a UNICODE_STRING structure onto
            // a LSAPR_UNICODE_STRING structure.
            //

            RtlCopyMemory(
                &TrustInformation.Name,
                &WellKnownSids[WellKnownSidIndex].DomainName,
                sizeof(UNICODE_STRING)
                );

            //
            // If the Sid has been recognized as a Well Known Sid and
            // is either a Domain Sid or has a well-known name, count
            // it as being mapped and add the Built-in Domain to the
            // Referenced Domain List.
            //

            if ((OutputNames[SidNumber].Use == SidTypeDomain) ||
                (OutputNames[SidNumber].Name.Length != 0)) {

                UnmappedSidsRemaining--;

                //
                // Make an entry in the list of Referenced Domains.  Note
                // that in the case of well-known Sids, the Prefix Sid
                // may or may not be the Sid of a Domain.  For those well
                // known Sids whose prefix Sid is not a domain Sid, the
                // Name field in the Trust Information has been set to the
                // NULL string.
                //

                Status = LsapDbLookupAddListReferencedDomains(
                             ReferencedDomains,
                             &TrustInformation,
                             (PLONG) &OutputNames[SidNumber].DomainIndex
                             );

                if (!NT_SUCCESS(Status)) {

                    break;
                }

            } else {

                //
                // The Sid is recognized as a Well Known Sid, but is
                // not a Domain Sid and does not have a Well Known Name
                // (signified by a zero length name string).  Filter this
                // out.
                //

                OutputNames[SidNumber].Use = SidTypeUnknown;
                OutputNames[SidNumber].Name.Length = (USHORT) 0;
                OutputNames[SidNumber].Name.MaximumLength = (USHORT) 0;
                OutputNames[SidNumber].Name.Buffer = NULL;
            }

            //
            // If memory was allocated for a Prefix Sid, free it.  Note that
            // the LsapDbLookupAddListTrustedDomains routine will have made
            // a copy of the Sid.
            //

            if (PrefixSid != NULL) {

                MIDL_user_free(PrefixSid);
                PrefixSid = NULL;
            }
        }
    }

    if (!NT_SUCCESS( Status )) {

        goto LookupIsolatedWellKnownSidsError;
    }

LookupIsolatedWellKnownSidsFinish:

    //
    // If there is a final PrefixSid buffer, free it.
    //

    if (PrefixSid != NULL) {

        MIDL_user_free(PrefixSid);
        PrefixSid = NULL;
    }

    //
    // Delete the Free List, freeing buffers on the list if an error
    // occurred.
    //

    LsapMmCleanupFreeList( &FreeList, CleanupFreeListOptions );

    //
    // Return output parameters.
    //

    *MappedCount = Count - UnmappedSidsRemaining;
    *CompletelyUnmappedCount = UnmappedSidsRemaining;
    return(Status);

LookupIsolatedWellKnownSidsError:

    //
    // Specify that buffers on Free List are to be freed.
    //

    CleanupFreeListOptions |= LSAP_MM_FREE_BUFFERS;
    goto LookupIsolatedWellKnownSidsFinish;
}


BOOLEAN
LsapDbLookupIndexWellKnownSid(
    IN PLSAPR_SID Sid,
    OUT PLSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
    )

/*++

Routine Description:

    This function looks up a Sid to determine if it is well-known.  If so,
    an index into the table of well-known Sids is returned.

Arguments:

    Sid - Pointer to Sid to be looked up.

    WellKnownSidIndex - Pointer to variable that will receive the
        index of the Sid if well known.

Return Value:

    BOOLEAN - TRUE if the Sid is well-known, else FALSE

--*/

{
    LSAP_WELL_KNOWN_SID_INDEX Index;

    //
    // Scan the table of well-known Sids looking for a match.
    //

    for(Index = LsapWorldSidIndex; Index<LsapDummyLastSidIndex; Index++) {

        //
        // Allow NULL entries in the table of well-known Sids for now.
        //

        if (WellKnownSids[Index].Sid == NULL) {

            continue;
        }

        //
        // If a match is found, return the index to the caller.
        //

        if (RtlEqualSid((PSID) Sid, WellKnownSids[Index].Sid)) {

            *WellKnownSidIndex = Index;
            return TRUE;
        }
    }

    //
    // The Sid is not a well-known Sid.  Return FALSE.
    //

    return FALSE;
}


ULONG LsapDbGetSizeTextSid(
    IN PSID Sid
    )

/*++

Routine Description:

    This function computes the size of ASCIIZ buffer required for a
    Sid in character form.  Temporarily, the size returned is an over-
    estimate, because 9 digits are allowed for the decimal equivalent
    of each 32-bit SubAuthority value.

Arguments:

    Sid - Pointer to Sid to be sized

Return Value:

    ULONG - The required size of buffer is returned.

--*/

{
    ULONG TextSidSize = 0;

    //
    // Count the Sid prefix and revision "S-rev-".  The revision is
    // assumed to not exceed 2 digits.
    //

    TextSidSize = sizeof("S-nn-");

    //
    // Add in the size of the identifier authority
    //

    TextSidSize += 15;   // log base 10 of 48 (= 6-byte number)

    //
    // If the Sid has SubAuthorities, count 9 bytes for each one
    //

    if (((PLSAPR_SID) Sid)->SubAuthorityCount > 0) {

        TextSidSize += (ULONG)
           (9 * ((PLSAPR_SID) Sid)->SubAuthorityCount);
    }

    return TextSidSize;
}


NTSTATUS
LsapDbSidToTextSid(
    IN PSID Sid,
    OUT PSZ TextSid
    )

/*++

Routine Description:

    This function converts a Sid to character text and places it in the
    supplied buffer.  The buffer is assumed to be of sufficient size, as
    can be computed by calling LsapDbGetSizeTextSid().

Arguments:

    Sid - Pointer to Sid to be converted.

    TextSid - Optional pointer to the buffer in which the converted
        Sid will be placed as an ASCIIZ.  A NULL pointer ma

Return Value:

    NTSTATUS - Standard Nt Result Code

--*/

{
    PLSAPR_SID ISid = Sid;
    ULONG Index;
    ULONG IdentifierAuthorityValue;
    UCHAR Buffer[LSAP_MAX_SIZE_TEXT_SID];

    sprintf(Buffer, "S-%u-", (USHORT) ISid->Revision );
    strcpy(TextSid, Buffer);

    if ((ISid->IdentifierAuthority.Value[0] != 0) ||
        (ISid->IdentifierAuthority.Value[1] != 0)) {

        sprintf(Buffer, "0x%02hx%02hx%02hx%02hx%02hx%02hx",
                    (USHORT)ISid->IdentifierAuthority.Value[0],
                    (USHORT)ISid->IdentifierAuthority.Value[1],
                    (USHORT)ISid->IdentifierAuthority.Value[2],
                    (USHORT)ISid->IdentifierAuthority.Value[3],
                    (USHORT)ISid->IdentifierAuthority.Value[4],
                    (USHORT)ISid->IdentifierAuthority.Value[5] );
        strcat(TextSid, Buffer);

    } else {

        IdentifierAuthorityValue =
            (ULONG)ISid->IdentifierAuthority.Value[5]          +
            (ULONG)(ISid->IdentifierAuthority.Value[4] <<  8)  +
            (ULONG)(ISid->IdentifierAuthority.Value[3] << 16)  +
            (ULONG)(ISid->IdentifierAuthority.Value[2] << 24);
        sprintf(Buffer, "%lu", IdentifierAuthorityValue);
        strcat(TextSid, Buffer);
    }

    //
    // Now format the Sub Authorities (if any) as text.
    //

    for (Index = 0; Index < (ULONG) ISid->SubAuthorityCount; Index++ ) {

        sprintf(Buffer, "-%lu", ISid->SubAuthority[Index]);
        strcat(TextSid, Buffer);
    }

    return STATUS_SUCCESS;
}


NTSTATUS
LsapDbSidToUnicodeSid(
    IN PSID Sid,
    OUT PUNICODE_STRING SidU,
    IN BOOLEAN AllocateDestinationString
    )

/*++

Routine Description:

    This function converts a Sid to Unicode form and optionally allocates
    (via MIDL_user_allocate) memory for the string buffer.

Arguments:

    Sid - Pointer to Sid to be translated.

    SidU - Pointer to Unicode string that will receive the Unicode
        Sid text.

    AllocateDestinationString - If TRUE, the buffer for the destination
        string will be allocated.  If FALSE, it is assummed that the
        destination Unicode string references a buffer of sufficient size.

Return Value:

--*/

{
    NTSTATUS Status;
    ULONG TextSidSize;
    PSZ TextSid;
    ANSI_STRING SidAnsi;

    //
    // First, query the amount of memory required for a buffer that
    // will hold the Sid as an ASCIIZ character string.
    //

    TextSidSize = LsapDbGetSizeTextSid(Sid);

    //
    // Now allocate a buffer for the Text Sid.
    //

    TextSid = LsapAllocateLsaHeap(TextSidSize);

    if (TextSid == NULL) {

        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Convert the Sid to ASCIIZ and place in the buffer.
    //

    Status = LsapDbSidToTextSid(Sid, TextSid);

    if (!NT_SUCCESS(Status)) {

        LsapFreeLsaHeap(TextSid);
        return Status;
    }

    //
    // Now convert the text Sid to Unicode form via ANSI string form.
    // If we are to allocate the output buffer, do so via the
    // midl_USER_allocate routine.
    //

    RtlInitString(&SidAnsi, TextSid);

    if (AllocateDestinationString) {

        SidU->MaximumLength = (USHORT) RtlAnsiStringToUnicodeSize(&SidAnsi);
        SidU->Buffer = MIDL_user_allocate( SidU->MaximumLength );
        SidU->Length = 0;
    }

    //
    // Now convert the Ansi String to a Unicode string.  The buffer is
    // already allocated.  Free Text Sid buffer before checking conversion
    // status.
    //

    Status = RtlAnsiStringToUnicodeString(SidU, &SidAnsi, FALSE);
    LsapFreeLsaHeap(TextSid);

    if (!NT_SUCCESS(Status)) {

        if (AllocateDestinationString) {

            MIDL_user_free(SidU->Buffer);
        }
    }

    return Status;
}


NTSTATUS
LsapDbLookupTranslateUnknownSids(
    IN ULONG Count,
    IN PLSAPR_SID *Sids,
    IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
    IN ULONG MappedCount
    )

/*++

Routine Description:

    This function translates unmapped Sids to a character representation.
    If the Domain of a Sid is unknown, the entire Sid is translated,
    otherwise the Relative Id only is translated.

Parameters:

    Count - Specifies the number of Sids in the array.

    Sids - Pointer to an array of Sids.  Some of these will already
        have been translated.

    ReferencedDomains - Pointer to Referenced Domains List header.

    TranslatedNames - Pointer to structure that references the array of
        translated names.  The nth element of the referenced array
        corresponds to the nth Sid in the Sids array.  Some of the
        Sids may be already translated and will be ignored.  Those that are
        not yet translated have zero length Unicode structures with NULL
        buffer pointers.  Already translated Sids are ignored.

    MappedCount - Specifies the number of Sids that have already been
        translated.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    ULONG SidIndex;
    ULONG UnmappedCount;
    PSID Sid;
    UNICODE_STRING NameU;
    PLSA_TRANSLATED_NAME Names = (PLSA_TRANSLATED_NAME) TranslatedNames->Names;
    LSAP_MM_FREE_LIST FreeList;
    ULONG CleanupFreeListOptions = (ULONG) 0;
    UnmappedCount = Count - MappedCount;

    //
    // Create FreeList
    //

    Status = LsapMmCreateFreeList(&FreeList, UnmappedCount);

    if (!NT_SUCCESS(Status)) {

        goto TranslateUnknownSidsError;
    }

    //
    // Examine the array of Sids, looking for Unknown ones to translate.
    // Translate any Unknown ones found to character representations,
    // and stop either when all of them have been accounted for, or when
    // the end of the array is reached.
    //

    if (MappedCount == Count) {

        goto TranslateUnknownSidsFinish;
    }

    if (MappedCount > Count) {

        goto TranslateUnknownSidsError;
    }

    for (SidIndex = 0, UnmappedCount = Count - MappedCount;
         (SidIndex < Count) && (UnmappedCount > 0);
         SidIndex++) {

        Sid = Sids[SidIndex];

        //
        // If the Sid has already been mapped, ignore it.
        //

        if (Names[SidIndex].Use != SidTypeUnknown) {

            continue;
        }

        //
        // Found an unmapped Sid.  If the domain is known, convert the
        // Relative Id of the Sid to a Unicode String, limited to 8
        // characters and with leading zeros.
        //

        if (Names[SidIndex].DomainIndex >= 0) {

            //
            // Convert the Relative Id to a Unicode Name and store in
            // the Translation.
            //

            Status = LsapRtlSidToUnicodeRid( Sid, &NameU );

            if (!NT_SUCCESS(Status)) {

                goto TranslateUnknownSidsError;
            }

        } else {

            //
            // The Domain is unknown.  In this case, convert the whole Sid
            // to the standard character representation.
            //

            Status = LsapRtlConvertSidToUnicodeString( Sid, &NameU );

            if (!NT_SUCCESS(Status)) {

                goto TranslateUnknownSidsError;
            }
        }

        //
        // Copy the Unicode Name to the output, allocating memory for
        // its buffer via MIDL_user_allocate
        //

        Status = LsapRpcCopyUnicodeString(
                     &FreeList,
                     &Names[SidIndex].Name,
                     &NameU
                     );

        RtlFreeUnicodeString(&NameU);

        if (!NT_SUCCESS(Status)) {

            goto TranslateUnknownSidsError;
        }

        //
        // Decrement the remaining Unmapped Count
        //

        UnmappedCount--;
    }

TranslateUnknownSidsFinish:

    //
    // Delete the Free List, freeing buffers on the list if an error
    // occurred.
    //

    LsapMmCleanupFreeList(&FreeList, CleanupFreeListOptions);
    return(Status);

TranslateUnknownSidsError:

    //
    // Specify that buffers on Free List are to be freed.
    //

    CleanupFreeListOptions |= LSAP_MM_FREE_BUFFERS;
    goto TranslateUnknownSidsFinish;
}


NTSTATUS
LsarLookupNames(
    IN LSAPR_HANDLE PolicyHandle,
    IN ULONG Count,
    IN PLSAPR_UNICODE_STRING Names,
    OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN LSAP_LOOKUP_LEVEL LookupLevel,
    IN OUT PULONG MappedCount
    )

/*++

Routine Description:

    This function is the LSA server worker routine for the LsaLookupNames
    API.

    The LsaLookupNames API attempts to translate names of domains, users,
    groups or aliases to Sids.  The caller must have POLICY_LOOKUP_NAMES
    access to the Policy object.

    Names may be either isolated (e.g. JohnH) or composite names containing
    both the domain name and account name.  Composite names must include a
    backslash character separating the domain name from the account name
    (e.g. Acctg\JohnH).  An isolated name may be either an account name
    (user, group, or alias) or a domain name.

    Translation of isolated names introduces the possibility of name
    collisions (since the same name may be used in multiple domains).  An
    isolated name will be translated using the following algorithm:

    If the name is a well-known name (e.g. Local or Interactive), then the
    corresponding well-known Sid is returned.

    If the name is the Built-in Domain's name, then that domain's Sid
    will be returned.

    If the name is the Account Domain's name, then that domain's Sid
    will be returned.
       /
    If the name is the Primary Domain's name, then that domain's Sid will
    be returned.

    If the name is a user, group, or alias in the Built-in Domain, then the
    Sid of that account is returned.

    If the name is a user, group, or alias in the Primary Domain, then the
    Sid of that account is returned.

    Otherwise, the name is not translated.

    NOTE: Proxy, Machine, and Trust user accounts are not referenced
    for name translation.  Only normal user accounts are used for ID
    translation.  If translation of other account types is needed, then
    SAM services should be used directly.

Arguments:

    This function is the LSA server RPC worker routine for the
    LsaLookupNames API.

    PolicyHandle -  Handle from an LsaOpenPolicy call.

    Count - Specifies the number of names to be translated.

    Names - Pointer to an array of Count Unicode String structures
        specifying the names to be looked up and mapped to Sids.
        The strings may be names of User, Group or Alias accounts or
        domains.

    ReferencedDomains - Receives a pointer to a structure describing the
        domains used for the translation.  The entries in this structure
        are referenced by the structure returned via the Sids parameter.
        Unlike the Sids parameter, which contains an array entry for
        each translated name, this structure will only contain one
        component for each domain utilized in the translation.

        When this information is no longer needed, it must be released
        by passing the returned pointer to LsaFreeMemory().

    TranslatedSids - Pointer to a structure which will (or already) references an array of
        records describing each translated Sid.  The nth entry in this array
        provides a translation for the nth element in the Names parameter.

        When this information is no longer needed, it must be released
        by passing the returned pointer to LsaFreeMemory().

    LookupLevel - Specifies the Level of Lookup to be performed on this
        machine.  Values of this field are are follows:

        LsapLookupWksta - First Level Lookup performed on a workstation
            normally configured for Windows-Nt.   The lookup searches the
            Well-Known Sids/Names, and the Built-in Domain and Account Domain
            in the local SAM Database.  If not all Sids or Names are
            identified, performs a "handoff" of a Second level Lookup to the
            LSA running on a Controller for the workstation's Primary Domain
            (if any).

        LsapLookupPDC - Second Level Lookup performed on a Primary Domain
            Controller.  The lookup searches the Account Domain of the
            SAM Database on the controller.  If not all Sids or Names are
            found, the Trusted Domain List (TDL) is obtained from the
            LSA's Policy Database and Third Level lookups are performed
            via "handoff" to each Trusted Domain in the List.

        LsapLookupTDL - Third Level Lookup performed on a controller
            for a Trusted Domain.  The lookup searches the Account Domain of
            the SAM Database on the controller only.

        MappedCount - Pointer to location that contains a count of the Names
            mapped so far. On exit, this will be updated.


Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully and all Names have
            been translated to Sids.

        STATUS_SOME_NOT_MAPPED - At least one of the names provided was
            trasnlated to a Sid, but not all names could be translated. This
            is a success status.

        STATUS_NONE_MAPPED - None of the names provided could be translated
            to Sids.  This is an error status, but output is returned.  Such
            output includes partial translations of names whose domain could
            be identified.

        STATUS_ACCESS_DENIED - Caller does not have the appropriate access
            to complete the operation.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
            to complete the call.
--*/

{
    NTSTATUS Status, SecondaryStatus;
    BOOLEAN PolicyHandleReferencedHere = FALSE;
    LSAPR_HANDLE TrustedDomainHandle = NULL;
    PPOLICY_LSA_SERVER_ROLE PolicyLsaServerRoleInfo = NULL;
    PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
    LSA_HANDLE ControllerPolicyHandle = NULL;
    ULONG DomainIndex;
    LSAPR_TRUST_INFORMATION TrustInformation;
    LSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation;
    LSAPR_TRUST_INFORMATION AccountDomainTrustInformation;
    LSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation;
    ULONG NullNameCount = 0;
    ULONG NameIndex;
    PLSA_TRANSLATED_SID OutputSids;
    PLSAPR_TRUST_INFORMATION Domains = NULL;
    ULONG OutputSidsLength;
    ULONG CompletelyUnmappedCount = Count;
    ULONG IsolatedNameCount;
    ULONG MappedIsolatedNameCount = (ULONG) 0;
    ULONG LocalDomainsToSearch = 0;

    PLSAPR_UNICODE_STRING PrefixNames = NULL;
    PLSAPR_UNICODE_STRING SuffixNames = NULL;
    LSAPR_UNICODE_STRING BackSlash;

    BuiltInDomainTrustInformation.Name.Buffer = NULL;
    BuiltInDomainTrustInformation.Sid = NULL;

    AccountDomainTrustInformation.Name.Buffer = NULL;
    AccountDomainTrustInformation.Sid = NULL;

    PrimaryDomainTrustInformation.Name.Buffer = NULL;
    PrimaryDomainTrustInformation.Sid = NULL;

    //
    // If there are no completely unmapped Names remaining, return.
    //

    if (CompletelyUnmappedCount == (ULONG) 0) {

        goto LookupNamesFinish;
    }

    //
    // Names provided are either Isolated, consisting of a single
    // component, or composite, having the form
    //
    // <DomainName>\<SuffixName>
    //
    // Split the list of names into two separate arrays, one containing
    // the Domain Prefixes (or NULL strings) and the other array
    // containing the Terminal Names.  Both arrays are the same size
    // as the original.  First, allocate memory for the output arrays
    // of UNICODE_STRING structures.
    //

    Status = STATUS_INSUFFICIENT_RESOURCES;

    PrefixNames = MIDL_user_allocate( Count * sizeof( UNICODE_STRING ));

    if (PrefixNames == NULL) {

        goto LookupNamesError;
    }

    SuffixNames = MIDL_user_allocate( Count * sizeof( UNICODE_STRING ));

    if (SuffixNames == NULL) {

        goto LookupNamesError;
    }

    RtlInitUnicodeString( (PUNICODE_STRING) &BackSlash, L"\\" );

    LsapRtlSplitNames(
        (PUNICODE_STRING) Names,
        Count,
        (PUNICODE_STRING) &BackSlash,
        (PUNICODE_STRING) PrefixNames,
        (PUNICODE_STRING) SuffixNames
        );

    //
    // Count the Isolated Names.
    //

    IsolatedNameCount = (ULONG) 0;

    for (NameIndex = 0; NameIndex < Count; NameIndex++) {

        if (PrefixNames[NameIndex].Length == (USHORT) 0) {

            IsolatedNameCount++;
        }
    }

    //
    // Acquire the Lsa Database lock.  Verify that the connection handle is
    // valid, is of the expected type and has all of the desired accesses
    // granted.  Reference he handle.
    //

    Status = LsapDbReferenceObject(
                 PolicyHandle,
                 POLICY_LOOKUP_NAMES,
                 PolicyObject,
                 LSAP_DB_ACQUIRE_LOCK
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesError;
    }

    //
    // We can dereference the original PolicyHandle and release the lock on
    // the LSA Database; if we need to access the database again, we'll
    // use the trusted Lsa handle and the appropriate API will take
    // the lock as required.
    //

    Status = LsapDbDereferenceObject(
                 &PolicyHandle,
                 PolicyObject,
                 LSAP_DB_RELEASE_LOCK,
                 (SECURITY_DB_DELTA_TYPE) 0,
                 Status
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesError;
    }

    TranslatedSids->Sids = NULL;
    TranslatedSids->Entries = 0;
    *ReferencedDomains = NULL;

    //
    // Allocate Output Sids array buffer.
    //

    OutputSidsLength = Count * sizeof(LSA_TRANSLATED_SID);
    OutputSids = MIDL_user_allocate(OutputSidsLength);

    Status = STATUS_INSUFFICIENT_RESOURCES;

    if (OutputSids == NULL) {

        goto LookupNamesError;
    }

    Status = STATUS_SUCCESS;
    TranslatedSids->Entries = Count;
    TranslatedSids->Sids = OutputSids;

    //
    // Initialize the Output Sids array.  Zeroise all fields, then
    // Mark all of the Output Sids as being unknown initially and
    // set the DomainIndex fields to a negative number meaning
    // "no domain"
    //

    RtlZeroMemory( OutputSids, OutputSidsLength);

    for (NameIndex = 0; NameIndex < Count; NameIndex++) {

        OutputSids[NameIndex].Use = SidTypeUnknown;
        OutputSids[NameIndex].DomainIndex = LSA_UNKNOWN_INDEX;
    }

    //
    // Create an empty Referenced Domain List.
    //

    Status = LsapDbLookupCreateListReferencedDomains( ReferencedDomains, 0 );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesError;
    }

    //
    // Obtain the Trust Information for the
    // Built-in, Account and Primary Domains.
    //

    Status = LsapDbLookupLocalDomains(
                 &BuiltInDomainTrustInformation,
                 &AccountDomainTrustInformation,
                 &PrimaryDomainTrustInformation
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesError;
    }

    //
    // The local domains to be searched always include the Accounts
    // domain.  For initial lookup targets only, the BUILT_IN domain is
    // also searched.
    //

    LocalDomainsToSearch = LSAP_DB_SEARCH_ACCOUNT_DOMAIN;

    if (LookupLevel == LsapLookupWksta) {

        LocalDomainsToSearch |= LSAP_DB_SEARCH_BUILT_IN_DOMAIN;

        //
        // This is the lowest Lookup Level, normally targeted at a
        // Workstation.
        //

        if (IsolatedNameCount > (ULONG) 0) {

            Status = LsapDbLookupIsolatedNames(
                         Count,
                         IsolatedNameCount,
                         Names,
                         PrefixNames,
                         SuffixNames,
                         &BuiltInDomainTrustInformation,
                         &AccountDomainTrustInformation,
                         &PrimaryDomainTrustInformation,
                         *ReferencedDomains,
                         TranslatedSids,
                         MappedCount,
                         &CompletelyUnmappedCount,
                         &MappedIsolatedNameCount
                         );

            if (!NT_SUCCESS(Status)) {

                goto LookupNamesError;
            }
        }

        //
        // If all Names are now mapped or partially mapped, or only zero
        // length names remain, finish.
        //

        NullNameCount = 0;

        for( NameIndex = 0; NameIndex < Count; NameIndex++) {

            if (Names[NameIndex].Length == 0) {

                NullNameCount++;
            }
        }

        if (CompletelyUnmappedCount == NullNameCount) {

            goto LookupNamesFinish;
        }
    }

    //
    // There are some remaining unmapped Names.  They may belong to a
    // local SAM Domain.  Currently, there are two such domains, the
    // Built-in Domain and the Accounts Domain.  Search these
    // domains now, excluding the BUILT_IN domain from higher level
    // searches.
    //

    Status = LsapDbLookupNamesInLocalDomains(
                 Count,
                 Names,
                 PrefixNames,
                 SuffixNames,
                 &BuiltInDomainTrustInformation,
                 &AccountDomainTrustInformation,
                 *ReferencedDomains,
                 TranslatedSids,
                 MappedCount,
                 &CompletelyUnmappedCount,
                 LocalDomainsToSearch
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesError;
    }

    //
    // If all Names apart from NULL names are now mapped, finish.
    //

    if (CompletelyUnmappedCount == NullNameCount) {

        goto LookupNamesFinish;
    }

    //
    // Not all of the Names have been identified in the local domain(s).
    // The next step in the search depends on the level of this lookup
    // and how we are configured as follows:
    //
    // Lookup Level         Configuration       Lookup search next
    //
    // LsapLookupWksta      Win Nt              Primary Domain
    //                      LanMan Nt           Trusted Domains
    //
    // LsapLookupPDC        Win Nt              error
    //                      LanMan Nt           Trusted Domains
    //
    // LsaLookupTDL         Win Nt              error
    //                      LanMan Nt           none
    //

    if (LookupLevel == LsapLookupWksta) {

        if (LsapProductType != NtProductLanManNt) {

            //
            // If there is no Primary Domain as in the case of a WORKGROUP,
            // just finish up.  Set a default result code STATUS_SUCCESS.
            //

            Status = STATUS_SUCCESS;

            if (PrimaryDomainTrustInformation.Sid == NULL) {

                goto LookupNamesFinish;
            }

            //
            // There is a Primary Domain.  Search it for Names.  Since a
            // Primary Domain is also a Trusted Domain, we use the
            // Trusted Domain search routine.  This routine will "hand off"
            // the search to a Domain Controller's LSA.
            //

            RtlCopyMemory(
                &TrustInformation.Name,
                &PrimaryDomainTrustInformation.Name,
                sizeof( UNICODE_STRING)
                );

            TrustInformation.Sid = (PSID) PrimaryDomainTrustInformation.Sid;

            Status = LsapDbLookupNamesInPrimaryDomain(
                         Count,
                         Names,
                         PrefixNames,
                         SuffixNames,
                         &TrustInformation,
                         *ReferencedDomains,
                         TranslatedSids,
                         LsapLookupPDC,
                         MappedCount,
                         &CompletelyUnmappedCount
                         );

            if (!NT_SUCCESS(Status)) {

                goto LookupNamesError;
            }

            goto LookupNamesFinish;
        }
    }

    //
    // We reach here in two cases:
    //
    // * Initial Level lookups targeted at DC's
    // * Higher Level Lookups (must be targeted at DC's)
    //
    // For the highest level lookup, that on an individual TDC, there
    // is no more searching to do, since we have already searched the
    // Accounts Domain and we do not follow trust relationships on DC's
    // beyond one level.
    //

    if (LookupLevel == LsapLookupTDL) {

        goto LookupNamesFinish;
    }

    //
    // We are either the initial target of the lookup but not configured
    // as a workstation, or we are the target of a Primary Domain
    // level lookup.  In either case, we must be configured as a DC.
    //

    Status = STATUS_DOMAIN_CTRLR_CONFIG_ERROR;

    if (LsapProductType != NtProductLanManNt) {

        goto LookupNamesError;
    }

    //
    // Search all of the Trusted Domains
    //

    Status = LsapDbLookupNamesInTrustedDomains(
                 Count,
                 Names,
                 PrefixNames,
                 SuffixNames,
                 *ReferencedDomains,
                 TranslatedSids,
                 LsapLookupTDL,
                 MappedCount,
                 &CompletelyUnmappedCount
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesError;
    }

LookupNamesFinish:

    //
    // If some but not all Names were mapped, return informational status
    // STATUS_SOME_NOT_MAPPED.  If no Names were mapped, return error
    // STATUS_NONE_MAPPED. Note that we expect and STATUS_NONE_MAPPED
    // errors returned by called routines to have been suppressed before
    // we get here.  The reason for this is that we need to calculate
    // the return Status based on the whole set of Names, not some subset
    //

    if (NT_SUCCESS(Status)) {

        if (*MappedCount < Count) {

            Status = STATUS_SOME_NOT_MAPPED;

            if (*MappedCount == 0) {

                Status = STATUS_NONE_MAPPED;
            }
        }
    }

    //
    // If we opened a handle to the Primary Domain's TrustedDomain
    // object, close it.
    //

    if (TrustedDomainHandle != NULL) {

        SecondaryStatus = LsarClose( &TrustedDomainHandle );
        TrustedDomainHandle == NULL;

        //
        // If the close failed, go to error, returning its status if the
        // primary status was a success code.
        //

        if (!NT_SUCCESS(SecondaryStatus)) {

            goto LookupNamesError;
        }
    }

    //
    // If necessary, free memory allocated for the Account and
    // Primary Domain Name and Sid buffers.  The memory for the Built-In
    // Domain Name and Sid buffers is not freed since these are global
    // data in the Well Known Sid table.
    //

    if ( AccountDomainTrustInformation.Name.Buffer != NULL ) {

        MIDL_user_free( AccountDomainTrustInformation.Name.Buffer );
    }

    if ( AccountDomainTrustInformation.Sid != NULL ) {

        MIDL_user_free( AccountDomainTrustInformation.Sid );
    }

    if ( PrimaryDomainTrustInformation.Name.Buffer != NULL ) {

        MIDL_user_free( PrimaryDomainTrustInformation.Name.Buffer );
    }

    if ( PrimaryDomainTrustInformation.Sid != NULL ) {

        MIDL_user_free( PrimaryDomainTrustInformation.Sid );
    }

    //
    // If necessary, free the arrays of PrefixNames and SuffixNames
    //

    if (PrefixNames != NULL) {

        MIDL_user_free(PrefixNames);
    }

    if (SuffixNames != NULL) {

        MIDL_user_free(SuffixNames);
    }

    return(Status);

LookupNamesError:

    //
    // If the LookupLevel is the lowest (Workstation Level) free up
    // the Sids and Referenced Domains arrays.
    //

    if (LookupLevel == LsapLookupWksta) {

        //
        // If necessary, free the Sids array.
        //

        if (TranslatedSids->Sids != NULL) {

            MIDL_user_free( TranslatedSids->Sids );
            TranslatedSids->Sids = NULL;
        }

        //
        // If necessary, free the Referenced Domain List.
        //

        if (*ReferencedDomains != NULL) {

            Domains = (*ReferencedDomains)->Domains;

            if (Domains != NULL) {

                for (DomainIndex = 0;
                     DomainIndex < (*ReferencedDomains)->Entries;
                     DomainIndex++) {

                    if (Domains[ DomainIndex ].Name.Buffer != NULL) {

                        MIDL_user_free( Domains[ DomainIndex ].Name.Buffer );
                        Domains[ DomainIndex ].Name.Buffer == NULL;
                    }

                    if (Domains[ DomainIndex ].Sid != NULL) {

                        MIDL_user_free( Domains[ DomainIndex ].Sid );
                        Domains[ DomainIndex ].Sid == NULL;
                    }
                }
            }

            MIDL_user_free( *ReferencedDomains );
            *ReferencedDomains = NULL;
        }
    }

    //
    // If the primary status was a success code, but the secondary
    // status was an error, propagate the secondary status.
    //

    if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {

        Status = SecondaryStatus;
    }

    goto LookupNamesFinish;
}


NTSTATUS
LsapDbEnumerateNames(
    IN LSAPR_HANDLE ContainerHandle,
    IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
    IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
    OUT PLSAP_DB_NAME_ENUMERATION_BUFFER DbEnumerationBuffer,
    IN ULONG PreferedMaximumLength
    )

/*++

Routine Description:

    This function enumerates Names of objects of a given type within a container
    object.  Since there may be more information than can be returned in a
    single call of the routine, multiple calls can be made to get all of the
    information.  To support this feature, the caller is provided with a
    handle that can be used across calls.  On the initial call,
    EnumerationContext should point to a variable that has been initialized
    to 0.

Arguments:

    ContainerHandle -  Handle to a container object.

    ObjectTypeId - Type of object to be enumerated.  The type must be one
        for which all objects have Names.

    EnumerationContext - API-specific handle to allow multiple calls
        (see Routine Description above).

    DbEnumerationBuffer - Receives a pointer to a structure that will receive
        the count of entries returned in an enumeration information array, and
        a pointer to the array.  Currently, the only information returned is
        the object Names.  These Names may be used together with object type to
        open the objects and obtain any further information available.

    PreferedMaximumLength - prefered maximum length of returned data (in 8-bit
        bytes).  This is not a hard upper limit, but serves as a guide.  Due to
        data conversion between systems with different natural data sizes, the
        actual amount of data returned may be greater than this value.

    CountReturned - Pointer to variable which will receive a count of the
        entries returned.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_ACCESS_DENIED - Caller does not have the appropriate access
            to complete the operation.

        STATUS_NO_MORE_ENTRIES - There are no more entries.  This warning
            is returned if there are no more objects to enumerate.  Note that
            zero or more objects may be enumerated on a call that returns this
            reply.
--*/

{
    NTSTATUS Status;
    LSAP_DB_ENUMERATION_ELEMENT LastElement;
    PLSAP_DB_ENUMERATION_ELEMENT FirstElement, NextElement, FreeElement;
    ULONG DataLengthUsed;
    ULONG ThisBufferLength;
    PUNICODE_STRING Names = NULL;
    BOOLEAN PreferedMaximumReached = FALSE;
    ULONG EntriesRead;
    ULONG Index, EnumerationIndex;
    BOOLEAN TrustedClient = ((LSAP_DB_HANDLE) ContainerHandle)->Trusted;

    LastElement.Next = NULL;
    FirstElement = &LastElement;

    //
    // If no enumeration buffer provided, return an error.
    //

    if ( (!ARGUMENT_PRESENT(DbEnumerationBuffer)) ||
         (!ARGUMENT_PRESENT(EnumerationContext ))  ) {

        return(STATUS_INVALID_PARAMETER);
    }


    //
    // Enumerate objects, stopping when the length of data to be returned
    // reaches or exceeds the Prefered Maximum Length, or reaches the
    // absolute maximum allowed for LSA object enumerations.  We allow
    // the last object enumerated to bring the total amount of data to
    // be returned beyond the Prefered Maximum Length, but not beyond the
    // absolute maximum length.
    //

    EnumerationIndex = *EnumerationContext;

    for(DataLengthUsed = 0, EntriesRead = 0;
        DataLengthUsed < PreferedMaximumLength;
        DataLengthUsed += ThisBufferLength, EntriesRead++) {

        //
        // If the absolute maximum length has been exceeded, back off
        // the last object enumerated.
        //

        if ((DataLengthUsed > LSA_MAXIMUM_ENUMERATION_LENGTH) &&
            (!TrustedClient)) {

            FirstElement = NextElement->Next;
            MIDL_user_free(NextElement);
            break;
        }

        //
        // Allocate memory for next enumeration element.  Set the Name
        // field to NULL for cleanup purposes.
        //

        NextElement = MIDL_user_allocate(sizeof (LSAP_DB_ENUMERATION_ELEMENT));

        if (NextElement == NULL) {

            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        //
        // Find the next object's Name, and fill in its object information.
        // Note that memory will be allocated via MIDL_user_allocate
        // and must be freed when no longer required.
        //

        Status = LsapDbFindNextName(
                     ContainerHandle,
                     &EnumerationIndex,
                     ObjectTypeId,
                     (PLSAPR_UNICODE_STRING) &NextElement->Name
                     );

        //
        // Stop the enumeration if any error or warning occurs.  Note
        // that the warning STATUS_NO_MORE_ENTRIES will be returned when
        // we've gone beyond the last index.
        //

        if (Status != STATUS_SUCCESS) {

            break;
        }

        //
        // Get the length of the data allocated for the object's Name
        //

        ThisBufferLength = NextElement->Name.Length;

        //
        // Link the object just found to the front of the enumeration list
        //

        NextElement->Next = FirstElement;
        FirstElement = NextElement;
    }

    //
    // If an error other than STATUS_NO_MORE_ENTRIES occurred, return it.
    //

    if ((Status != STATUS_NO_MORE_ENTRIES) && !NT_SUCCESS(Status)) {

        goto EnumerateNamesError;
    }

    //
    // The enumeration is complete or has terminated because of return
    // buffer limitations.  If no entries were read, return.
    //

    if (EntriesRead != 0) {


        //
        // Some entries were read, allocate an information buffer for returning
        // them.
        //

        Names = MIDL_user_allocate( sizeof (UNICODE_STRING) * EntriesRead );

        if (Names == NULL) {

            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto EnumerateNamesError;
        }

        //
        // Memory was successfully allocated for the return buffer.
        // Copy in the enumerated Names.
        //

        for (NextElement = FirstElement, Index = 0;
            NextElement != &LastElement;
            NextElement = NextElement->Next, Index++) {

            ASSERT(Index < EntriesRead);

            Names[Index] = NextElement->Name;
        }

        Status = STATUS_SUCCESS;

    } else {

        //
        // No entries available this call.
        //

        Status = STATUS_NO_MORE_ENTRIES;

    }

EnumerateNamesFinish:

    //
    // Free the enumeration element structures (if any).
    //

    for (NextElement = FirstElement; NextElement != &LastElement;) {

        //
        // If an error has occurred, dispose of memory allocated
        // for any Names.
        //

        if (!(NT_SUCCESS(Status) || (Status == STATUS_NO_MORE_ENTRIES))) {

            if (NextElement->Name.Buffer != NULL) {

                MIDL_user_free(NextElement->Name.Buffer);
            }
        }

        //
        // Free the memory allocated for the enumeration element.
        //

        FreeElement = NextElement;
        NextElement = NextElement->Next;

        MIDL_user_free(FreeElement);
    }

    //
    // Fill in return enumeration structure (0 and NULL in error case).
    //

    DbEnumerationBuffer->EntriesRead = EntriesRead;
    DbEnumerationBuffer->Names = Names;
    *EnumerationContext = EnumerationIndex;

    return(Status);

EnumerateNamesError:

    //
    // If necessary, free memory allocated for returning the Names.
    //

    if (Names != NULL) {

        MIDL_user_free( Names );
        Names = NULL;
    }

    goto EnumerateNamesFinish;
}


VOID
LsapDbUpdateCountCompUnmappedNames(
    OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN OUT PULONG CompletelyUnmappedCount
    )

/*++

Routine Description:

    This function updates the count of completely unmapped names in a
    name lookup operation.  A name is completely unmapped if its domain
    is unknown.

Arguments:

    TranslatedSids - Pointer to a structure which will be initialized to
        reference an array of records describing each translated Sid.  The
        nth entry in this array provides a translation for the nth element in
        the Names parameter.

        When this information is no longer needed, it must be released
        by passing the returned pointer to LsaFreeMemory().

    CompletelyUnmappedCount - Pointer to location that will receive
        a count of completely unmapped Sids.  A Name is completely unmapped
        if it is isolated and unknown, or is composite and its Domain Prefix
        component is not recognized as a Domain Name.

Return Values:

    None

--*/

{
    ULONG Count = TranslatedSids->Entries;
    ULONG SidIndex;
    ULONG UpdatedCompletelyUnmappedCount = 0;

    for (SidIndex = 0; SidIndex < Count; SidIndex++) {

        if (TranslatedSids->Sids[SidIndex].DomainIndex == LSA_UNKNOWN_INDEX) {

            UpdatedCompletelyUnmappedCount++;
        }
    }

    ASSERT(UpdatedCompletelyUnmappedCount <= *CompletelyUnmappedCount);
    *CompletelyUnmappedCount = UpdatedCompletelyUnmappedCount;
}


NTSTATUS
LsapDbFindNextName(
    IN LSAPR_HANDLE ContainerHandle,
    IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
    IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
    OUT PLSAPR_UNICODE_STRING NextName
    )

/*++

Routine Description:

    This function finds the next Name of object of a given type within a
    container object.  The given object type must be one where objects
    have Names.  The Names returned can be used on subsequent open calls to
    access the objects.

Arguments:

    ContainerHandle - Handle to container object.

    EnumerationContext - Pointer to a variable containing the index of
        the object to be found.  A zero value indicates that the first
        object is to be found.

    ObjectTypeId - Type of the objects whose Names are being enumerated.
        Ccurrently, this is restricted to objects (such as Secret Objects)
        that are accessed by Name only.

    NextName - Pointer to Unicode String that will be initialized to
        reference the next Name found.

Return Value:

    NTSTATUS - Standard Nt Result Code

        STATUS_INVALID_HANDLE - Invalid ContainerHandle specified

        STATUS_NO_MORE_ENTRIES - Warning that no more entries exist.
--*/

{
    NTSTATUS Status, SecondaryStatus;
    ULONG NameKeyValueLength = 0;
    LSAPR_UNICODE_STRING SubKeyNameU;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE ContDirKeyHandle = NULL;


    //
    // Setup object attributes for opening the appropriate Containing
    // Directory.  For example, if we're looking for Account objects,
    // the containing Directory is "Accounts".  The Unicode strings for
    // containing Directories are set up during Lsa Initialization.
    //

    InitializeObjectAttributes(
        &ObjectAttributes,
        &LsapDbContDirs[ObjectTypeId],
        OBJ_CASE_INSENSITIVE,
        ((LSAP_DB_HANDLE) ContainerHandle)->KeyHandle,
        NULL
        );

    //
    // If the object type is not accessed by Name only, return an error.
    // Currently, only Secret objects have this property.
    //


    if (ObjectTypeId != SecretObject) {
        return(STATUS_INVALID_PARAMETER);
    }

    Status = RtlpNtOpenKey(
                 &ContDirKeyHandle,
                 KEY_READ,
                 &ObjectAttributes,
                 0
                 );

    if (NT_SUCCESS(Status)) {

        //
        // Initialize the Unicode String in which the next object's Logical Name
        // will be returned.  The Logical Name of an object equals its Registry
        // Key relative to its Containing Directory, and is also equal to
        // the Relative Id of the object represented in character form as an
        // 8-digit number with leading zeros.
        //
        // NOTE: The size of buffer allocated for the Logical Name must be
        // calculated dynamically when the Registry supports long names, because
        // it is possible that the Logical Name of an object will be equal to a
        // character representation of the full Name, not just the Relative Id
        // part.
        //

        SubKeyNameU.MaximumLength = (USHORT) LSAP_DB_LOGICAL_NAME_MAX_LENGTH;
        SubKeyNameU.Length = 0;
        SubKeyNameU.Buffer = LsapAllocateLsaHeap(SubKeyNameU.MaximumLength);

        if (SubKeyNameU.Buffer == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        } else {

            //
            // Now enumerate the next subkey.
            //

            Status = RtlpNtEnumerateSubKey(
                         ContDirKeyHandle,
                         (PUNICODE_STRING) &SubKeyNameU,
                         *EnumerationContext,
                         NULL
                         );

            if (NT_SUCCESS(Status)) {

                (*EnumerationContext)++;

                //
                // Return the Name.
                //

                *NextName = SubKeyNameU;

            } else {

                //
                // Not successful - free the subkey name buffer
                // Note that STATUS_NO_MORE_ENTRIES is a warning
                // (not a success) code.
                //

                LsapFreeLsaHeap( SubKeyNameU.Buffer );

                //
                // Set the out parameter so RPC doesn't try
                // to return anything.
                //

                NextName->Length = NextName->MaximumLength = 0;
                NextName->Buffer = NULL;

            }

        }

        //
        // Close the containing directory handle
        //

        SecondaryStatus = NtClose(ContDirKeyHandle);
        ASSERT(NT_SUCCESS(SecondaryStatus));
    }

    return(Status);

}


NTSTATUS
LsapDbLookupIsolatedNames(
    IN ULONG Count,
    IN ULONG IsolatedNameCount,
    IN PLSAPR_UNICODE_STRING Names,
    IN PLSAPR_UNICODE_STRING PrefixNames,
    IN PLSAPR_UNICODE_STRING SuffixNames,
    IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
    IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
    IN PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount,
    IN OUT PULONG MappedIsolatedNameCount
    )

/*++

Routine Description:

    This function attempts to identify isolated names as the names of well known
    Sids or Domains present on the Lookup Path.

    Names may be either isolated (e.g. JohnH) or composite names containing
    both the domain name and account name.  Composite names must include a
    backslash character separating the domain name from the account name
    (e.g. Acctg\JohnH).  An isolated name may be either an account name
    (user, group, or alias) or a domain name.

    Translation of isolated names introduces the possibility of name
    collisions (since the same name may be used in multiple domains).  An
    isolated name will be translated using the following algorithm:

    If the name is a well-known name (e.g. Local or Interactive), then the
    corresponding well-known Sid is returned.

    If the name is the Built-in Domain's name, then that domain's Sid
    will be returned.

    If the name is the Account Domain's name, then that domain's Sid
    will be returned.

    If the name is the Primary Domain's name, then that domain's Sid will
    be returned.

    If the name is the name of one of the Primary Domain's Trusted Domains,
    then that domain's Sid will be returned.

    If the name is a user, group, or alias in the Built-in Domain, then the
    Sid of that account is returned.

    If the name is a user, group, or alias in the Primary Domain, then the
    Sid of that account is returned.

    Otherwise, the name is not translated.

    NOTE: Proxy, Machine, and Trust user accounts are not referenced
    for name translation.  Only normal user accounts are used for ID
    translation.  If translation of other account types is needed, then
    SAM services should be used directly.

Arguments:

    Count - Specifies the number of names to be translated.

    IsolatedNameCount - Specifies the number of Isolated Names present
        in the list of Names.  An Isolated Name is a Name that contains
        only one component, e.g. "ScottBi".  Names such as "NtDev\ScottBi"
        containing two components are called Composite Names.

    Names - Pointer to an array of Count Unicode String structures
        specifying the names to be looked up and mapped to Sids.
        The strings may be names of User, Group or Alias accounts or
        domains.

    PrefixNames - Pointer to an array of Count Unicode String structures
        containing the Prefix portions of the Names.  Names having no
        Prefix are called Isolated Names.  For these, the Unicode String
        structure is set to contain a zero Length.

    SuffixNames - Pointer to an array of Count Unicode String structures
        containing the Suffix portions of the Names.

    ReferencedDomains - Pointer to a structure in which the list of domains
        used in the translation is maintained.  The entries in this structure
        are referenced by the structure returned via the Sids parameter.
        Unlike the Sids parameter, which contains an array entry for each
        translated name, this structure will only contain one component for
        each domain utilized in the translation.

    TranslatedSids - Pointer to a structure in which the translations to Sids
        corresponding to the Names specified on Names is maintained.  The
        nth entry in this array provides a translation (where known) for the
        nth element in the Names parameter.

    MappedCount - Pointer to location in which a count of the Names that
        have been completely translated is maintained.

    CompletelyUnmappedCount - Pointer to a location in which a count of the
        Names that have not been translated (either partially, by identification
        of a Domain Prefix, or completely) is maintained.

    MappedIsolatedNameCount - Pointer to a location in which a count of the
        Isolated Names that have been mapped is maintained.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_ACCESS_DENIED - Caller does not have the appropriate access
            to complete the operation.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
            to complete the call.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    //
    // First, lookup any Isolated Well Known Names
    //

    Status = LsapDbLookupIsolatedWellKnownNames(
                 Count,
                 IsolatedNameCount,
                 Names,
                 PrefixNames,
                 SuffixNames,
                 ReferencedDomains,
                 TranslatedSids,
                 MappedCount,
                 CompletelyUnmappedCount,
                 MappedIsolatedNameCount
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupIsolatedNamesError;
    }

    //
    // If all of the Isolated Names have now been mapped, finish.
    //

    if (*MappedIsolatedNameCount == IsolatedNameCount) {

        goto LookupIsolatedNamesFinish;
    }

    //
    // Next, attempt to identify Isolated Names as Domain Names
    //

    Status = LsapDbLookupIsolatedDomainNames(
                 Count,
                 IsolatedNameCount,
                 Names,
                 PrefixNames,
                 SuffixNames,
                 BuiltInDomainTrustInformation,
                 AccountDomainTrustInformation,
                 PrimaryDomainTrustInformation,
                 ReferencedDomains,
                 TranslatedSids,
                 MappedCount,
                 CompletelyUnmappedCount,
                 MappedIsolatedNameCount
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupIsolatedNamesError;
    }

LookupIsolatedNamesFinish:

    return(Status);

LookupIsolatedNamesError:

    goto LookupIsolatedNamesFinish;
}


NTSTATUS
LsapDbLookupIsolatedWellKnownNames(
    IN ULONG Count,
    IN ULONG IsolatedNameCount,
    IN PLSAPR_UNICODE_STRING Names,
    IN PLSAPR_UNICODE_STRING PrefixNames,
    IN PLSAPR_UNICODE_STRING SuffixNames,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount,
    IN OUT PULONG MappedIsolatedNameCount
    )

/*++

Routine Description:

    This function attempts to identify isolated names as the names of well known
    Sids.

    Names may be either isolated (e.g. JohnH) or composite names containing
    both the domain name and account name.  Composite names must include a
    backslash character separating the domain name from the account name
    (e.g. Acctg\JohnH).  An isolated name may be either an account name
    (user, group, or alias) or a domain name.

    Translation of isolated names introduces the possibility of name
    collisions (since the same name may be used in multiple domains).  An
    isolated name will be translated using the following algorithm:

    If the name is a well-known name (e.g. Local or Interactive), then the
    corresponding well-known Sid is returned.

    If the name is the Built-in Domain's name, then that domain's Sid
    will be returned.

    If the name is the Account Domain's name, then that domain's Sid
    will be returned.

    If the name is the Primary Domain's name, then that domain's Sid will
    be returned.

    If the name is the name of one of the Primary Domain's Trusted Domains,
    then that domain's Sid will be returned.

    If the name is a user, group, or alias in the Built-in Domain, then the
    Sid of that account is returned.

    If the name is a user, group, or alias in the Primary Domain, then the
    Sid of that account is returned.

    Otherwise, the name is not translated.

    NOTE: Proxy, Machine, and Trust user accounts are not referenced
    for name translation.  Only normal user accounts are used for ID
    translation.  If translation of other account types is needed, then
    SAM services should be used directly.

Arguments:

    Count - Specifies the number of names to be translated.

    IsolatedNameCount - Specifies the number of Isolated Names present
        in the list of Names.  An Isolated Name is a Name that contains
        only one component, e.g. "ScottBi".  Names such as "NtDev\ScottBi"
        containing two components are called Composite Names.

    Names - Pointer to an array of Count Unicode String structures
        specifying the names to be looked up and mapped to Sids.
        The strings may be names of User, Group or Alias accounts or
        domains.

    PrefixNames - Pointer to an array of Count Unicode String structures
        containing the Prefix portions of the Names.  Names having no
        Prefix are called Isolated Names.  For these, the Unicode String
        structure is set to contain a zero Length.

    SuffixNames - Pointer to an array of Count Unicode String structures
        containing the Suffix portions of the Names.

    ReferencedDomains - Pointer to a structure in which the list of domains
        used in the translation is maintained.  The entries in this structure
        are referenced by the structure returned via the Sids parameter.
        Unlike the Sids parameter, which contains an array entry for each
        translated name, this structure will only contain one component for
        each domain utilized in the translation.

    TranslatedSids - Pointer to a structure in which the translations to Sids
        corresponding to the Names specified on Names is maintained.  The
        nth entry in this array provides a translation (where known) for the
        nth element in the Names parameter.

    MappedCount - Pointer to location in which a count of the Names that
        have been completely translated is maintained.

    CompletelyUnmappedCount - Pointer to a location in which a count of the
        Names that have not been translated (either partially, by identification
        of a Domain Prefix, or completely) is maintained.

    MappedIsolatedNameCount - Pointer to a location in which a count of the
        Isolated Names that have been mapped is maintained.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Names may remain partially or completely unmapped.

        STATUS_ACCESS_DENIED - Caller does not have the appropriate access
            to complete the operation.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
            to complete the call.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG UpdatedMappedCount;
    ULONG NameNumber;
    ULONG UnmappedNamesRemaining;
    PLSA_TRANSLATED_SID OutputSids;
    LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex;
    LSAPR_TRUST_INFORMATION TrustInformation;
    UCHAR SubAuthorityCount;
    PLSAPR_SID Sid = NULL;
    PLSAPR_SID PrefixSid = NULL;
    ULONG PrefixSidLength;
    ULONG RelativeId;
    OutputSids = TranslatedSids->Sids;

    //
    // Initialize output parameters.
    //

    *MappedCount = UpdatedMappedCount = 0;
    UnmappedNamesRemaining = Count - UpdatedMappedCount;

    //
    // Attempt to identify Names as Well Known Isolated Names
    //

    for (NameNumber = 0;
         (NameNumber < Count) && (UnmappedNamesRemaining > 0);
         NameNumber++) {

        //
        // Examine the next entry in the Names array.  If the corresponding
        // translated Sid entry has SidTypeUnknown for its Use field, the
        // name has not been translated.
        //

        if (OutputSids[NameNumber].Use == SidTypeUnknown) {

            //
            // Attempt to identify the name as the name of a Well Known Sid
            // by using the Well Known Sids table.  We skip entries in the
            // table for Sids that are also in the Built In domain.  For
            // those, we drop through to the Built in Domain search.  Note
            // that only one of these, the Administrators alias is currently
            // present in the table.
            //

            if (LsapDbLookupIndexWellKnownName(
                    &Names[NameNumber],
                    &WellKnownSidIndex
                    ) && (WellKnownSidIndex != LsapAliasAdminsSidIndex)) {

                //
                // Name is identified.  Obtain its Sid.  If the
                // SubAuthorityCount for the Sid is positive, extract the
                // Relative Id and place in the translated Sid entry,
                // otherwise store LSA_UNKNOWN_INDEX there.
                //

                Sid = LsapDbWellKnownSid(WellKnownSidIndex);

                SubAuthorityCount = *RtlSubAuthorityCountSid((PSID) Sid);

                RelativeId = LSA_UNKNOWN_ID;

                PrefixSid = NULL;

                //
                // Get the Sid's Use.
                //

                OutputSids[NameNumber].Use =
                    LsapDbWellKnownSidNameUse(WellKnownSidIndex);

                //
                // If the Sid is a Domain Sid, store pointer to
                // it in the Trust Information.
                //

                if (OutputSids[NameNumber].Use == SidTypeDomain) {

                    TrustInformation.Sid = Sid;

                } else {

                    //
                    // The Sid is not a domain Sid.  Construct the Relative Id
                    // and Prefix Sid.  This is equal to the original Sid
                    // excluding the lowest subauthority (Relative id).
                    //

                    if (SubAuthorityCount > 0) {

                        RelativeId = *RtlSubAuthoritySid((PSID) Sid, SubAuthorityCount - 1);
                    }

                    PrefixSidLength = RtlLengthRequiredSid(
                                          SubAuthorityCount - 1
                                          );


                    PrefixSid = MIDL_user_allocate( PrefixSidLength );

                    if (PrefixSid == NULL) {

                        Status = STATUS_INSUFFICIENT_RESOURCES;
                        break;
                    }


                    RtlCopyMemory( PrefixSid, Sid, PrefixSidLength );

                    (*RtlSubAuthorityCountSid( (PSID) PrefixSid ))--;

                    TrustInformation.Sid = PrefixSid;
                }

                //
                // Set the Relative Id.  For a Domain Sid this is set to the
                // Unknown Value.
                //

                OutputSids[NameNumber].RelativeId = RelativeId;

                //
                // Lookup this Domain Sid or Prefix Sid in the Referenced Domain
                // List.  If it is already there, return the DomainIndex for the
                // existing entry and free up the memory allocated for the
                // Prefix Sid (if any).
                //

                if (LsapDbLookupListReferencedDomains(
                        ReferencedDomains,
                        TrustInformation.Sid,
                        (PLONG) &OutputSids[NameNumber].DomainIndex
                        )) {

                    UnmappedNamesRemaining--;

                    if (PrefixSid != NULL) {

                        MIDL_user_free( PrefixSid );
                        PrefixSid = NULL;
                    }

                    continue;
                }

                //
                // This Domain or Prefix Sid is not currently on the
                // Referenced Domain List.  Complete a Trust Information
                // entry and add it to the List.  Copy in the Domain Name
                // (Domain Sids) or NULL string.  Note that we use
                // RtlCopyMemory to copy a UNICODE_STRING structure onto
                // a LSAPR_UNICODE_STRING structure.
                //

                RtlCopyMemory(
                    &TrustInformation.Name,
                    &WellKnownSids[WellKnownSidIndex].DomainName,
                    sizeof(UNICODE_STRING)
                    );

                //
                // Make an entry in the list of Referenced Domains.  Note
                // that in the case of well-known Sids, the Prefix Sid
                // may or may not be the Sid of a Domain.  For those well
                // known Sids whose Prefix Sid is not a domain Sid, the
                // Name field in the Trust Information has been set to the
                // NULL string.
                //

                Status = LsapDbLookupAddListReferencedDomains(
                             ReferencedDomains,
                             &TrustInformation,
                             (PLONG) &OutputSids[NameNumber].DomainIndex
                             );

                if (!NT_SUCCESS(Status)) {

                    break;
                }

                //
                // If we allocated memory for a Prefix Sid, free it.
                //

                if (PrefixSid != NULL) {

                    MIDL_user_free( PrefixSid );
                    PrefixSid = NULL;
                }

                UnmappedNamesRemaining--;
                (*MappedIsolatedNameCount)++;
            }
        }
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupIsolatedWellKnownNamesError;
    }

    //
    // Set the output parameters in the success case..
    //

    TranslatedSids->Sids = OutputSids;
    TranslatedSids->Entries = Count;
    *MappedCount = Count - UnmappedNamesRemaining;
    *CompletelyUnmappedCount = UnmappedNamesRemaining;

LookupIsolatedWellKnownNamesFinish:

    //
    // If we still have memory allocated for the a Prefix Sid, free it.
    //

    if (PrefixSid != NULL) {

        MIDL_user_free( PrefixSid );
        PrefixSid = NULL;
    }

    return(Status);

LookupIsolatedWellKnownNamesError:

    goto LookupIsolatedWellKnownNamesFinish;
}


BOOLEAN
LsapDbLookupIndexWellKnownName(
    IN OPTIONAL PLSAPR_UNICODE_STRING Name,
    OUT PLSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
    )

/*++

Routine Description:

    This function looks up a Name to determine if it is well-known.  If so,
    an index into the table of well-known Sids is returned.

Arguments:

    Name - Pointer to Name to be looked up.  If a NULL pointer or
        pointer to a zero length string is specified, FALSE will
        always be returned.

    WellKnownSidIndex - Pointer to variable that will receive the
        index of the Name if well known.

Return Value:

    BOOLEAN - TRUE if the Name is well-known, else FALSE

--*/

{
    LSAP_WELL_KNOWN_SID_INDEX Index;
    PLSAPR_UNICODE_STRING MatchName = NULL;
    BOOLEAN BooleanStatus = FALSE;

    if ((!ARGUMENT_PRESENT(Name)) || Name->Length == 0 ) {

        return(FALSE);
    }

    //
    // Scan the table of well-known Sids looking for a match on Name.
    //

    for(Index = LsapWorldSidIndex; Index<LsapDummyLastSidIndex; Index++) {

        //
        // Allow NULL entries in the table of well-known Sids for now.
        //

        if (WellKnownSids[Index].Sid == NULL) {

            continue;
        }

        //
        // If the current entry in the table of Well Known Sids
        // is for a Domain Sid, match the name with the DomainName
        // field.  Otherwise, match it with the Name field.
        //

        if (WellKnownSids[Index].Use == SidTypeDomain) {

            MatchName = (PLSAPR_UNICODE_STRING) &WellKnownSids[Index].DomainName;

            if (RtlEqualDomainName(
                   (PUNICODE_STRING) Name,
                   (PUNICODE_STRING) MatchName
                   )) {

                //
                // If a match is found, return the index to the caller.
                //

                BooleanStatus = TRUE;
                break;
            }

        } else {

            MatchName = (PLSAPR_UNICODE_STRING) &WellKnownSids[Index].Name;

            if (RtlEqualUnicodeString(
                   (PUNICODE_STRING) Name,
                   (PUNICODE_STRING) MatchName,
                   TRUE
                   )) {

                //
                // If a match is found, return the index to the caller.
                //

                BooleanStatus = TRUE;
                break;
            }
        }
    }

    *WellKnownSidIndex = Index;

    return(BooleanStatus);
}


PUNICODE_STRING
LsapDbWellKnownSidName(
    IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
    )

/*++

Routine Description:

    This function returns the Unicode Name of a Well Known Sid.

Arguments:

    WellKnownSidIndex - Index into the Well Known Sid information table.
    It is the caller's responsibility to ensure that the given index
    is valid.

Return Value:

    PUNICODE_STRING Pointer to the name of the Well Known Sid.

--*/

{
    //
    // If the Sid is a Domain Sid, its name is contained within the
    // DomainName field in the Well Known Sids table.  If the Sid is not a
    // Domain Sid, its name is contained within the Name field of the entry.
    //

    if (WellKnownSids[WellKnownSidIndex].Use == SidTypeDomain) {

        return(&WellKnownSids[WellKnownSidIndex].DomainName);

    } else {

        return(&WellKnownSids[WellKnownSidIndex].Name);
    }
}


NTSTATUS
LsapDbLookupIsolatedDomainNames(
    IN ULONG Count,
    IN ULONG IsolatedNameCount,
    IN PLSAPR_UNICODE_STRING Names,
    IN PLSAPR_UNICODE_STRING PrefixNames,
    IN PLSAPR_UNICODE_STRING SuffixNames,
    IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
    IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
    IN PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount,
    IN OUT PULONG MappedIsolatedNameCount
    )

/*++

Routine Description:

    This function attempts to identify isolated names as the names of Domains.

    Names may be either isolated (e.g. JohnH) or composite names containing
    both the domain name and account name.  Composite names must include a
    backslash character separating the domain name from the account name
    (e.g. Acctg\JohnH).  An isolated name may be either an account name
    (user, group, or alias) or a domain name.

    Translation of isolated names introduces the possibility of name
    collisions (since the same name may be used in multiple domains).  An
    isolated name will be translated using the following algorithm:

    If the name is a well-known name (e.g. Local or Interactive), then the
    corresponding well-known Sid is returned.

    If the name is the Built-in Domain's name, then that domain's Sid
    will be returned.

    If the name is the Account Domain's name, then that domain's Sid
    will be returned.

    If the name is the Primary Domain's name, then that domain's Sid will
    be returned.

    If the name is the name of one of the Primary Domain's Trusted Domains,
    then that domain's Sid will be returned.

    If the name is a user, group, or alias in the Built-in Domain, then the
    Sid of that account is returned.

    If the name is a user, group, or alias in the Primary Domain, then the
    Sid of that account is returned.

    Otherwise, the name is not translated.

    NOTE: Proxy, Machine, and Trust user accounts are not referenced
    for name translation.  Only normal user accounts are used for ID
    translation.  If translation of other account types is needed, then
    SAM services should be used directly.

Arguments:

    Count - Specifies the number of names to be translated.

    IsolatedNameCount - Specifies the number of Isolated Names present
        in the list of Names.  An Isolated Name is a Name that contains
        only one component, e.g. "ScottBi".  Names such as "NtDev\ScottBi"
        containing two components are called Composite Names.

    Names - Pointer to an array of Count Unicode String structures
        specifying the names to be looked up and mapped to Sids.
        The strings may be names of User, Group or Alias accounts or
        domains.

    PrefixNames - Pointer to an array of Count Unicode String structures
        containing the Prefix portions of the Names.  Names having no
        Prefix are called Isolated Names.  For these, the Unicode String
        structure is set to contain a zero Length.

    SuffixNames - Pointer to an array of Count Unicode String structures
        containing the Suffix portions of the Names.

    ReferencedDomains - Pointer to a structure in which the list of domains
        used in the translation is maintained.  The entries in this structure
        are referenced by the structure returned via the Sids parameter.
        Unlike the Sids parameter, which contains an array entry for each
        translated name, this structure will only contain one component for
        each domain utilized in the translation.

    TranslatedSids - Pointer to a structure in which the translations to Sids
        corresponding to the Names specified on Names is maintained.  The
        nth entry in this array provides a translation (where known) for the
        nth element in the Names parameter.

    MappedCount - Pointer to location in which a count of the Names that
        have been completely translated is maintained.

    CompletelyUnmappedCount - Pointer to a location in which a count of the
        Names that have not been translated (either partially, by identification
        of a Domain Prefix, or completely) is maintained.

    MappedIsolatedNameCount - Pointer to a location in which a count of the
        Isolated Names that have been mapped is maintained.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_ACCESS_DENIED - Caller does not have the appropriate access
            to complete the operation.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
            to complete the call.
--*/

{
    NTSTATUS
        Status = STATUS_SUCCESS,
        IgnoreStatus;

    NTSTATUS SecondaryStatus = STATUS_SUCCESS;

    ULONG
        NameIndex;

    PLSAPR_POLICY_ACCOUNT_DOM_INFO
        PolicyAccountDomainInfo = NULL;

    PLSAPR_TRUST_INFORMATION
        TrustInformation = NULL;

    LSAP_DB_TRUSTED_DOMAIN_LIST
        PDTrustedDomainList;

    PLSAP_DB_TRUSTED_DOMAIN_LIST
        TrustedDomainList = NULL;

    PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION
        TrustedDomainListSection = NULL;

    BOOLEAN
        PrimaryDomainAccessible = TRUE;

    LSA_HANDLE
        ControllerPolicyHandle = NULL;

    //
    // Search for Isolated Names that match the Built-In Domain Name,
    // Account Domain Name or one of the Primary Domain's Trusted Domain
    // Names.
    //

    PDTrustedDomainList.Valid = FALSE;

    for (NameIndex = 0;
         (NameIndex < Count) && (*MappedIsolatedNameCount < IsolatedNameCount);
         NameIndex++) {

        //
        // Skip this name if already mapped or partially mapped.
        //

        if (!LsapDbCompletelyUnmappedSid(&TranslatedSids->Sids[NameIndex])) {

            continue;
        }

        //
        // Skip this name if composite.
        //

        if (PrefixNames[ NameIndex ].Length != (USHORT) 0) {

            continue;
        }

        //
        // We've found an Isolated Name.  First check if it is the
        // name of the Built In Domain.
        //

        Status = LsapDbLookupIsolatedDomainName(
                     NameIndex,
                     &SuffixNames[NameIndex],
                     BuiltInDomainTrustInformation,
                     ReferencedDomains,
                     TranslatedSids,
                     MappedCount,
                     CompletelyUnmappedCount,
                     MappedIsolatedNameCount
                     );

        if (NT_SUCCESS(Status)) {

            continue;
        }

        if (Status != STATUS_NONE_MAPPED) {

            break;
        }

        Status = STATUS_SUCCESS;

        //
        // Isolated Name is not the name of the Built-in Domain.  See if
        // it is the name of the Accounts Domain.
        //

        Status = LsapDbLookupIsolatedDomainName(
                     NameIndex,
                     &SuffixNames[NameIndex],
                     AccountDomainTrustInformation,
                     ReferencedDomains,
                     TranslatedSids,
                     MappedCount,
                     CompletelyUnmappedCount,
                     MappedIsolatedNameCount
                     );

        if (NT_SUCCESS(Status)) {

            continue;
        }

        Status = STATUS_SUCCESS;

        //
        // If we are configured as a member of a Work Group, there is
        // no Primary or Trusted Domain List to search, so skip to next
        // name.  We are configured as a member of a Work Group if and
        // only if out PolicyPrimaryDomainInformation contains a NULL Sid.
        //

        if (PrimaryDomainTrustInformation->Sid == NULL) {

            continue;
        }

        //
        // Isolated Name is not the name of either the Built-in or Accounts
        // Domain.  Try the Primary Domain if this is different from the
        // Accounts Domain.
        //

        if (!RtlEqualDomainName(
                 (PUNICODE_STRING) &PrimaryDomainTrustInformation->Name,
                 (PUNICODE_STRING) &AccountDomainTrustInformation->Name
                 )) {


            Status = LsapDbLookupIsolatedDomainName(
                         NameIndex,
                         &SuffixNames[NameIndex],
                         PrimaryDomainTrustInformation,
                         ReferencedDomains,
                         TranslatedSids,
                         MappedCount,
                         CompletelyUnmappedCount,
                         MappedIsolatedNameCount
                         );

            if (NT_SUCCESS(Status)) {

                continue;
            }

            if (Status != STATUS_NONE_MAPPED) {

                break;
            }

            Status = STATUS_SUCCESS;
        }

        //
        // See if the Isolated Name matches one of the Primary Domain's
        // Trusted Domains or, if we are a DC, one of our Trusted Domains.
        // If we are a Workstation, we first need to enumerate the Trusted Domains
        // on the Primary Domain.  If the Name is a Trusted Domain,
        // translate it.
        //

        if (LsapProductType != NtProductLanManNt) {

            if ((!PDTrustedDomainList.Valid) && PrimaryDomainAccessible) {

                //
                // We are a Workstation.  Use the Trusted Domain List
                // for the Primary Domain after first constructing a copy
                // of it.
                //

                TrustedDomainList = &PDTrustedDomainList;

                //
                // Open the Policy object on some DC for the Primary Domain.
                //

                Status = LsapDbOpenPolicyTrustedDomain(
                             PrimaryDomainTrustInformation,
                             POLICY_VIEW_LOCAL_INFORMATION,
                             &ControllerPolicyHandle
                             );

                if (!NT_SUCCESS(Status)) {

                    //
                    // We are unable to connect to the Primary Domain.
                    // This simply means that we limit our search for Isolated
                    // Names to the local domains, so mask the error and continue.
                    //

                    Status = STATUS_SUCCESS;
                    PrimaryDomainAccessible = FALSE;
                    TrustedDomainList = NULL;
                    continue;
                }


                //
                // We've successfully opened a DC for the Primary Domain.
                // Now build a list of its Trusted Domains.
                //

                Status = LsapDbBuildTrustedDomainList(
                             ControllerPolicyHandle,
                             TrustedDomainList
                             );

                if (!NT_SUCCESS(Status)) {

                    PDTrustedDomainList.Valid = FALSE;
                    PrimaryDomainAccessible = FALSE;
                    SecondaryStatus = LsaClose(ControllerPolicyHandle);
                    ControllerPolicyHandle = NULL;
                    continue;
                }
            }

        } else {

            ASSERT (LsapProductType == NtProductLanManNt);

            //
            // We are a DC.  Use the Local Trusted Domain List.
            //

            TrustedDomainList = NULL;

        }

        //
        // Lookup the Isolated Name in the appropriate Trusted Domain List.
        //

        Status = LsapDbLookupNameTrustedDomainList(
                     TrustedDomainList,
                     &SuffixNames[NameIndex],
                     &TrustInformation
                     );

        if (!NT_SUCCESS(Status)) {

            if (Status != STATUS_NO_SUCH_DOMAIN) {

                break;
            }

            //
            // This Isolated Name is not the Name of a Trusted Domain.
            // Skip to the next Name.
            //

            Status = STATUS_SUCCESS;

            if ( ControllerPolicyHandle != NULL) {

                SecondaryStatus = LsaClose( ControllerPolicyHandle );
                ControllerPolicyHandle = NULL;

            }

            continue;
        }

        //
        // This Isolated Name is a Trusted Domain.  Translate it.
        //

        Status = LsapDbLookupIsolatedDomainName(
                     NameIndex,
                     &SuffixNames[NameIndex],
                     TrustInformation,
                     ReferencedDomains,
                     TranslatedSids,
                     MappedCount,
                     CompletelyUnmappedCount,
                     MappedIsolatedNameCount
                     );

        if (NT_SUCCESS(Status)) {

            if ( ControllerPolicyHandle != NULL) {

                SecondaryStatus = LsaClose( ControllerPolicyHandle );
                ControllerPolicyHandle = NULL;

            }

            continue;
        }

        if (Status != STATUS_NONE_MAPPED) {

            break;
        }

        Status = STATUS_SUCCESS;
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupIsolatedDomainNamesError;
    }

LookupIsolatedDomainNamesFinish:

    //
    // Clean up any trusted domain list we might have created.
    //

    if (PDTrustedDomainList.Valid) {
        IgnoreStatus = LsapDbDestroyTrustedDomainList( &PDTrustedDomainList );
        ASSERT(NT_SUCCESS(IgnoreStatus));
    }

    if ( ControllerPolicyHandle != NULL) {

        SecondaryStatus = LsaClose( ControllerPolicyHandle );
        ControllerPolicyHandle = NULL;

        if (!NT_SUCCESS(SecondaryStatus)) {

            if (NT_SUCCESS(Status)) {
                Status = SecondaryStatus;
            }
        }
    }


    return(Status);

LookupIsolatedDomainNamesError:

    goto LookupIsolatedDomainNamesFinish;
}


NTSTATUS
LsapDbLookupAddListReferencedDomains(
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    OUT PLONG DomainIndex
    )

/*++

Routine Description:

    This function searches a Referenced Domain List for an entry for a
    given domain and, if no entry exists, adds new entry.  If an entry
    id added, its index into the Referenced Domain List is returned,
    otherwise the index of the existing entry is returned.  If an entry
    needs to be added and there is insufficient room in the list provided
    for the new entry, the list will be created or grown as necessary.

Arguments:

    ReferencedDomains - Pointer to a structure in which the list of domains
        used in the translation is maintained.  The entries in this structure
        are referenced by the structure returned via the Sids parameter.
        Unlike the Sids parameter, which contains an array entry for each
        translated name, this structure will only contain one component for
        each domain utilized in the translation.

    TrustInformation - Points to Trust Information for the domain being
        added to the list.  On exit, the DomainIndex parameter will be set to the
        index of the entry on the Referenced Domain List; a negative
        value will be stored in the error case.

    DomainIndex - Pointer to location that receives the index of the
        newly added or existing entry for the domain within the
        Referenced Domain List.  In the error case, a negative value
        is returned.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.

--*/

{
    NTSTATUS Status;
    ULONG NextIndex;
    LSAPR_TRUST_INFORMATION OutputTrustInformation;

    OutputTrustInformation.Name.Buffer = NULL;
    OutputTrustInformation.Sid = NULL;

    Status = STATUS_INVALID_PARAMETER;

    if (ReferencedDomains == NULL) {

        goto AddListReferencedDomainsError;
    }

    Status = STATUS_SUCCESS;

    //
    // Search the existing list, trying to match the Domain Sid in the
    // provided Trust Information with the Domain Sid in a Referenced Domain
    // List entry.  If an entry is found with matching Sid, just return
    // that entry's index.
    //

    if (LsapDbLookupListReferencedDomains(
            ReferencedDomains,
            TrustInformation->Sid,
            DomainIndex
            )) {

        goto AddListReferencedDomainsFinish;
    }

    //
    // Check that there is enough room in the List provided for one more
    // entry.  If not, grow the list, copying and freeing the old.
    //

    Status = STATUS_SUCCESS;

    if (ReferencedDomains->Entries >= ReferencedDomains->MaxEntries) {

        Status = LsapDbLookupGrowListReferencedDomains(
                     ReferencedDomains,
                     ReferencedDomains->MaxEntries +
                     LSAP_DB_REF_DOMAIN_DELTA
                     );

        if (!NT_SUCCESS(Status)) {

            goto AddListReferencedDomainsError;
        }
    }

    //
    // We now have a Referenced Domain List with room for at least one more
    // entry.  Copy in the Trust Information.
    //

    NextIndex = ReferencedDomains->Entries;


    Status = LsapRpcCopyUnicodeString(
                 NULL,
                 (PUNICODE_STRING) &OutputTrustInformation.Name,
                 (PUNICODE_STRING) &TrustInformation->Name
                 );

    if (!NT_SUCCESS(Status)) {

        goto AddListReferencedDomainsError;
    }

    Status = LsapRpcCopySid(
                 NULL,
                 (PSID) &OutputTrustInformation.Sid,
                 (PSID) TrustInformation->Sid
                 );

    if (!NT_SUCCESS(Status)) {

        goto AddListReferencedDomainsError;
    }

    ReferencedDomains->Domains[NextIndex] = OutputTrustInformation;
    *DomainIndex = (LONG) NextIndex;
    ReferencedDomains->Entries++;

AddListReferencedDomainsFinish:

    return(Status);

AddListReferencedDomainsError:

    //
    // Cleanup buffers allocated for Output Trust Information structure.
    //

    if (OutputTrustInformation.Name.Buffer != NULL) {

        MIDL_user_free( OutputTrustInformation.Name.Buffer );
    }

    if (OutputTrustInformation.Sid != NULL) {

        MIDL_user_free( OutputTrustInformation.Sid );
    }

    goto AddListReferencedDomainsFinish;
}


NTSTATUS
LsapDbLookupCreateListReferencedDomains(
    OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
    IN ULONG InitialMaxEntries
    )

/*++

Routine Description:

    This function creates an empty Referenced Domain List.  The caller
    is responsible for cleaning up this list when no longer required.

Arguments:

    ReferencedDomains - Receives a pointer to the newly created empty
        Referenced Domain List.

    InitialMaxEntries - Initial maximum number of entries desired.

Return Values:

    NTSTATUS - Standard Nt Result Code.

        STATUS_SUCCESS - The call completed successfully.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient System Resources
            such as memory, to complete the call.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG DomainsLength;
    PLSAPR_TRUST_INFORMATION Domains = NULL;
    PVOID Buffers[2];
    ULONG BufferCount;
    ULONG Index;
    PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;

    //
    // Allocate the Referenced Domain List header.
    //

    BufferCount = 0;

    OutputReferencedDomains = MIDL_user_allocate(
                                  sizeof(LSAP_DB_REFERENCED_DOMAIN_LIST)
                                  );

    Status = STATUS_INSUFFICIENT_RESOURCES;

    if (OutputReferencedDomains == NULL) {

        goto CreateListReferencedDomainsError;
    }

    Status = STATUS_SUCCESS;

    Buffers[BufferCount] = OutputReferencedDomains;
    BufferCount++;

    //
    // If a non-zero initial entry count, allocate an array of Trust Information
    // entries.
    //

    if (InitialMaxEntries > 0) {

        DomainsLength = sizeof(LSA_TRUST_INFORMATION) * InitialMaxEntries;
        Domains = MIDL_user_allocate( DomainsLength );

        Status = STATUS_INSUFFICIENT_RESOURCES;

        if (Domains == NULL) {

            goto CreateListReferencedDomainsError;
        }

        Status = STATUS_SUCCESS;

        Buffers[BufferCount] = Domains;
        BufferCount++;

        RtlZeroMemory( Domains, DomainsLength );
    }

    //
    // Initialize the Referenced Domain List Header
    //

    OutputReferencedDomains->Entries = 0;
    OutputReferencedDomains->MaxEntries = InitialMaxEntries;
    OutputReferencedDomains->Domains = Domains;

CreateListReferencedDomainsFinish:

    *ReferencedDomains = OutputReferencedDomains;
    return(Status);

CreateListReferencedDomainsError:

    //
    // Free up buffers allocated bu this routine.
    //

    for (Index = 0; Index < BufferCount; Index++) {

        MIDL_user_free(Buffers[Index]);
    }

    goto CreateListReferencedDomainsFinish;
}


NTSTATUS
LsapDbLookupGrowListReferencedDomains(
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN ULONG MaxEntries
    )

/*++

Routine Description:

    This function expands a Referenced Domain List to contain the
    specified maximum number of entries.  The memory for the old Domains
    array is released.

Arguments:

    ReferencedDomains - Pointer to a Referenced Domain List.  This
        list references an array of zero or more Trust Information
        entries describing each of the domains referenced by the names.
        This array will be appended to/reallocated if necessary.

    MaxEntries - New maximum number of entries.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources to
            complete the call.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAPR_TRUST_INFORMATION NewDomainsInfo = NULL;
    PLSAPR_TRUST_INFORMATION OldDomainsInfo = NULL;
    ULONG OldDomainsInfoLength, NewDomainsInfoLength;

    if (ReferencedDomains->MaxEntries < MaxEntries) {

        NewDomainsInfoLength = MaxEntries * sizeof (LSA_TRUST_INFORMATION);
        OldDomainsInfoLength =
            ReferencedDomains->MaxEntries * sizeof (LSA_TRUST_INFORMATION);

        NewDomainsInfo = MIDL_user_allocate( NewDomainsInfoLength );

        Status = STATUS_INSUFFICIENT_RESOURCES;

        if (NewDomainsInfo == NULL) {

            goto GrowListReferencedDomainsError;
        }

        Status = STATUS_SUCCESS;

        //
        // If there was an existing Trust Information Array, copy it
        // to the newly allocated one and free it.
        //

        OldDomainsInfo = ReferencedDomains->Domains;

        if (OldDomainsInfo != NULL) {

            RtlCopyMemory( NewDomainsInfo, OldDomainsInfo, OldDomainsInfoLength );
            MIDL_user_free( OldDomainsInfo );
        }

        ReferencedDomains->Domains = NewDomainsInfo;
        ReferencedDomains->MaxEntries = MaxEntries;
    }

GrowListReferencedDomainsFinish:

    return(Status);

GrowListReferencedDomainsError:

    goto GrowListReferencedDomainsFinish;
}


BOOLEAN
LsapDbLookupListReferencedDomains(
    IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN PLSAPR_SID DomainSid,
    OUT PLONG DomainIndex
    )

/*++

Routine Description:

    This function searches a Referenced Domain List for a given domain
    and, if found, returns the index of the domain's entry in the list.
    If the domain is not found an error is returned and a negative value
    is returned.

Arguments:

    ReferencedDomains - Pointer to a Referenced Domain List.  This
        list references an array of zero or more Trust Information
        entries describing each of the domains referenced by the names.
        This array will be appended to/reallocated if necessary.

    DomainSid - Information containing the Domain's Sid.

    DomainIndex - Pointer to location that receives the index of the domain
        in the Referenced Domain List if the domin is found, otherwise
        a negative value.

Return Values:

    BOOLEAN - TRUE if entry found, else FALSE.

--*/

{
    BOOLEAN BooleanStatus = FALSE;
    LONG Index;
    LONG Entries;
    PLSAPR_TRUST_INFORMATION DomainsInfo;

    //
    // Search the Referenced Domain List by Sid or by Name
    //

    Entries = (LONG) ReferencedDomains->Entries;
    DomainsInfo = ReferencedDomains->Domains;
    *DomainIndex = LSA_UNKNOWN_INDEX;

    for (Index = 0; Index < (LONG) Entries; Index++) {

        if (RtlEqualSid( (PSID) DomainsInfo[Index].Sid, (PSID) DomainSid)) {

            BooleanStatus = TRUE;
            *DomainIndex = Index;
            break;
        }
    }

    return(BooleanStatus);
}


NTSTATUS
LsapDbLookupMergeDisjointReferencedDomains(
    IN OPTIONAL PLSAPR_REFERENCED_DOMAIN_LIST FirstReferencedDomainList,
    IN OPTIONAL PLSAPR_REFERENCED_DOMAIN_LIST SecondReferencedDomainList,
    OUT PLSAPR_REFERENCED_DOMAIN_LIST *OutputReferencedDomainList,
    IN ULONG Options
    )

/*++

Routine Description:

    This function merges disjoint Referenced Domain Lists, producing a third
    list.  The output list is always produced in non allocate(all_nodes) form.

Arguments:

    FirstReferencedDomainList - Pointer to first mergand.

    SecondReferencedDomainList - Pointer to second mergand.

    OutputReferencedDomainList - Receives a pointer to the output list.

    Options - Specifies optional actions

        LSAP_DB_USE_FIRST_MERGAND_GRAPH - Specifies that the resulting
            merged Referenced Domain List may reference the graph of
            pointers in the first Referenced Domain list.  This option
            is normally selected, since that graph has been allocated
            as individual nodes.

        LSAP_DB_USE_SECOND_MERGAND_GRAPH - Specifies that the resulting
            merged Referenced Domain List may reference the graph of
            pointers in the first Referenced Domain list.  This option
            is normally not selected, since that graph is usually allocated
            as all_nodes.

Return Values:

    NTSTATUS - Standard Nt Result Code.

--*/

{
    NTSTATUS Status;
    ULONG TotalEntries;
    ULONG FirstReferencedDomainListLength;
    ULONG SecondReferencedDomainListLength;
    ULONG FirstEntries, SecondEntries;
    LSAP_MM_FREE_LIST FreeList;
    ULONG NextEntry;
    ULONG MaximumFreeListEntries;
    ULONG CleanupFreeListOptions = (ULONG) 0;

    //
    // Calculate Size of output Referenced Domain List.
    //

    FirstEntries = (ULONG) 0;

    if (FirstReferencedDomainList != NULL) {

        FirstEntries = FirstReferencedDomainList->Entries;
    }

    SecondEntries = (ULONG) 0;

    if (SecondReferencedDomainList != NULL) {

        SecondEntries = SecondReferencedDomainList->Entries;
    }

    TotalEntries = FirstEntries + SecondEntries;

    //
    // Allocate a Free List for error cleanup.  We need two entries
    // per Referenced Domain List entry, one for the Domain Name buffer
    // and one for the Domain Sid.
    //

    MaximumFreeListEntries = (ULONG) 0;

    if (!(Options & LSAP_DB_USE_FIRST_MERGAND_GRAPH)) {

        MaximumFreeListEntries += 2*FirstEntries;
    }

    if (!(Options & LSAP_DB_USE_SECOND_MERGAND_GRAPH)) {

        MaximumFreeListEntries += 2*SecondEntries;
    }

    Status = LsapMmCreateFreeList( &FreeList, MaximumFreeListEntries );

    if (!NT_SUCCESS(Status)) {

        goto MergeDisjointDomainsError;
    }

    Status = LsapDbLookupCreateListReferencedDomains(
                 OutputReferencedDomainList,
                 TotalEntries
                 );

    if (!NT_SUCCESS(Status)) {

        goto MergeDisjointDomainsError;
    }

    //
    // Set the number of entries used.  We will use all of the entries,
    // so set this value to the Maximum number of Entries.
    //

    (*OutputReferencedDomainList)->Entries = TotalEntries;

    //
    // Copy in the entries (if any) from the first list.
    //

    FirstReferencedDomainListLength =
        FirstEntries * sizeof(LSA_TRUST_INFORMATION);

    if (FirstReferencedDomainListLength > (ULONG) 0) {

        if (Options & LSAP_DB_USE_FIRST_MERGAND_GRAPH) {

            RtlCopyMemory(
                (*OutputReferencedDomainList)->Domains,
                FirstReferencedDomainList->Domains,
                FirstReferencedDomainListLength
                );

        } else {

            //
            // The graph of the first Referenced Domain List must be
            // copied to separately allocated memory buffers.
            // Copy each of the Trust Information entries, allocating
            // individual memory buffers for each Domain Name and Sid.
            //

            for (NextEntry = 0; NextEntry < FirstEntries; NextEntry++) {

                Status = LsapRpcCopyUnicodeString(
                             &FreeList,
                             (PUNICODE_STRING) &((*OutputReferencedDomainList)->Domains[NextEntry].Name),
                             (PUNICODE_STRING) &FirstReferencedDomainList->Domains[NextEntry].Name
                             );

                if (!NT_SUCCESS(Status)) {

                    break;
                }

                Status = LsapRpcCopySid(
                             &FreeList,
                             (PSID) &((*OutputReferencedDomainList)->Domains[NextEntry].Sid),
                             (PSID) FirstReferencedDomainList->Domains[NextEntry].Sid
                             );

                if (!NT_SUCCESS(Status)) {

                    break;
                }
            }

            if (!NT_SUCCESS(Status)) {

                goto MergeDisjointDomainsError;
            }
        }
    }

    //
    // Copy in the entries (if any) from the second list.
    //

    SecondReferencedDomainListLength =
        SecondEntries * sizeof(LSA_TRUST_INFORMATION);

    if (SecondReferencedDomainListLength > (ULONG) 0) {

        if (Options & LSAP_DB_USE_SECOND_MERGAND_GRAPH) {

            RtlCopyMemory(
                (*OutputReferencedDomainList)->Domains + FirstReferencedDomainList->Entries,
                SecondReferencedDomainList->Domains,
                SecondReferencedDomainListLength
                );

        } else {

            //
            // Copy each of the Trust Information entries, allocating
            // individual memory buffers for each Domain Name and Sid.
            //

            for (NextEntry = 0; NextEntry < SecondEntries; NextEntry++) {

                Status = LsapRpcCopyUnicodeString(
                             &FreeList,
                             (PUNICODE_STRING) &((*OutputReferencedDomainList)->Domains[FirstEntries +NextEntry].Name),
                             (PUNICODE_STRING) &SecondReferencedDomainList->Domains[NextEntry].Name
                             );

                if (!NT_SUCCESS(Status)) {

                    break;
                }

                Status = LsapRpcCopySid(
                             &FreeList,
                             (PSID) &((*OutputReferencedDomainList)->Domains[FirstEntries +NextEntry].Sid),
                             (PSID) SecondReferencedDomainList->Domains[NextEntry].Sid
                             );

                if (!NT_SUCCESS(Status)) {

                    break;
                }
            }

            if (!NT_SUCCESS(Status)) {

                goto MergeDisjointDomainsError;
            }
        }
    }

MergeDisjointDomainsFinish:

    //
    // Delete the Free List, freeing buffers on the list if an error
    // occurred.
    //

    LsapMmCleanupFreeList( &FreeList, CleanupFreeListOptions );
    return(Status);

MergeDisjointDomainsError:

    //
    // Specify that buffers on Free List are to be freed.
    //

    CleanupFreeListOptions |= LSAP_MM_FREE_BUFFERS;
    goto MergeDisjointDomainsFinish;
}


NTSTATUS
LsapDbLookupSidsInLocalDomains(
    IN ULONG Count,
    IN PLSAPR_SID *Sids,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount,
    IN ULONG Options
    )

/*++

Routine Description:

    This function looks up Sids in the local SAM domains and attempts to
    translate them to names.  Currently, there are two local SAM domains,
    the Built-in domain (which has a well-known Sid and name) and the
    Account Domain (which has a configurable Sid and name).

Arguments:

    Count - Number of Sids in the Sids array,  Note that some of these
        may already have been mapped elsewhere, as specified by the
        MappedCount parameter.

    Sids - Pointer to array of pointers to Sids to be translated.
        Zero or all of the Sids may already have been translated
        elsewhere.  If any of the Sids have been translated, the
        Names parameter will point to a location containing a non-NULL
        array of Name translation structures corresponding to the
        Sids.  If the nth Sid has been translated, the nth name
        translation structure will contain either a non-NULL name
        or a non-negative offset into the Referenced Domain List.  If
        the nth Sid has not yet been translated, the nth name
        translation structure will contain a zero-length name string
        and a negative value for the Referenced Domain List index.

    ReferencedDomains - Pointer to a structure in which the list of domains
        used in the translation is maintained.  The entries in this structure
        are referenced by the structure returned via the Sids parameter.
        Unlike the Sids parameter, which contains an array entry for each
        translated name, this structure will only contain one component for
        each domain utilized in the translation.

    TranslatedNames - Pointer to a structure in which the translations to Names
        corresponding to the Sids specified on Sids is maintained.  The
        nth entry in this array provides a translation (where known) for the
        nth element in the Sids parameter.

    MappedCount - Pointer to location in which a count of the Names that
        have been completely translated is maintained.

    CompletelyUnmappedCount - Pointer to a location in which a count of the
        Names that have not been translated (either partially, by identification
        of a Domain Prefix, or completely) is maintained.

    Options - Specifies optional actions.

        LSAP_DB_SEARCH_BUILT_IN_DOMAIN - Search the Built In Domain

        LSAP_DB_SEARCH_ACCOUNT_DOMAIN - Search the Account Domain

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Sids may remain partially or completely unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.

        STATUS_INTERNAL_DB_ERROR - A corruption has been detected in
            the LSA Database.

        STATUS_INVALID_PARAMETER - Invalid parameter

            - No handle to the Policy object was provided on a request
              to search the Account Domain.
--*/

{
    NTSTATUS
        Status,
        SecondaryStatus = STATUS_SUCCESS;

    LSAPR_TRUST_INFORMATION
        TrustInformation;

    ULONG
        UpdatedMappedCount = *MappedCount;

    LSAPR_HANDLE
        TrustedPolicyHandle = NULL;

    LSAP_WELL_KNOWN_SID_INDEX
        WellKnownSidIndex;

    PLSAPR_POLICY_ACCOUNT_DOM_INFO
        PolicyAccountDomainInfo = NULL;


    //
    // If there are no completely unmapped Sids remaining, return.
    //

    if (*CompletelyUnmappedCount == (ULONG) 0) {

        goto LookupSidsInLocalDomainsFinish;
    }



    //
    // If requested, lookup Sids in the BUILT-IN Domain.
    //

    if (Options & LSAP_DB_SEARCH_BUILT_IN_DOMAIN) {

        //
        // Set up the Trust Information structure for this domain.
        //

        TrustInformation.Sid = LsapBuiltInDomainSid;

        Status = STATUS_INTERNAL_DB_ERROR;

        if (!LsapDbLookupIndexWellKnownSid(
                LsapBuiltInDomainSid,
                &WellKnownSidIndex
                )) {

            goto LookupSidsInLocalDomainsError;
        }

        Status = STATUS_SUCCESS;

        //
        // Obtain the name of the Built In Domain from the table of
        // Well Known Sids.  It suffices to copy the Unicode structures
        // since we do not need a separate copy of the name buffer.
        //

        TrustInformation.Name = *((PLSAPR_UNICODE_STRING)
                                 LsapDbWellKnownSidName(WellKnownSidIndex));

        Status = LsapDbLookupSidsInLocalDomain(
                     LSAP_DB_SEARCH_BUILT_IN_DOMAIN,
                     Count,
                     Sids,
                     &TrustInformation,
                     ReferencedDomains,
                     TranslatedNames,
                     &UpdatedMappedCount,
                     CompletelyUnmappedCount
                     );

        if (!NT_SUCCESS(Status)) {

            goto LookupSidsInLocalDomainsError;
        }

        //
        // If all Sids are now mapped or partially mapped, finish.
        //

        if (*CompletelyUnmappedCount == (ULONG) 0) {

            goto LookupSidsInLocalDomainsFinish;
        }
    }

    //
    // If requested, search the Account Domain.
    //

    if (Options & LSAP_DB_SEARCH_ACCOUNT_DOMAIN) {

        //
        // The Sid and Name of the Account Domain are both configurable, and
        // we need to obtain them from the Policy Object.  Now obtain the
        // Account Domain Sid and Name by querying the appropriate
        // Policy Information Class.
        //

        Status = LsarQueryInformationPolicy(
                     LsapPolicyHandle,
                     PolicyAccountDomainInformation,
                     (PLSAPR_POLICY_INFORMATION *) &PolicyAccountDomainInfo
                     );

        if (!NT_SUCCESS(Status)) {

            goto LookupSidsInLocalDomainsError;
        }

        //
        // Set up the Trust Information structure for the Account Domain.
        //

        TrustInformation.Sid = PolicyAccountDomainInfo->DomainSid;

        RtlCopyMemory(
            &TrustInformation.Name,
            &PolicyAccountDomainInfo->DomainName,
            sizeof (UNICODE_STRING)
            );

        //
        // Now search the Account Domain for more Sid translations.
        //

        Status = LsapDbLookupSidsInLocalDomain(
                     LSAP_DB_SEARCH_ACCOUNT_DOMAIN,
                     Count,
                     Sids,
                     &TrustInformation,
                     ReferencedDomains,
                     TranslatedNames,
                     &UpdatedMappedCount,
                     CompletelyUnmappedCount
                     );

        if (!NT_SUCCESS(Status)) {

            goto LookupSidsInLocalDomainsError;
        }
    }

LookupSidsInLocalDomainsFinish:

    //
    // If necessary, free the Policy Account Domain Info
    //

    if (PolicyAccountDomainInfo != NULL) {

        LsaIFree_LSAPR_POLICY_INFORMATION(
            PolicyAccountDomainInformation,
            (PLSAPR_POLICY_INFORMATION) PolicyAccountDomainInfo
            );

        PolicyAccountDomainInfo = NULL;
    }


    //
    // Return the updated total count of Sids mapped.
    //

    *MappedCount = UpdatedMappedCount;
    return(Status);

LookupSidsInLocalDomainsError:


    if (NT_SUCCESS(Status)) {

        Status = SecondaryStatus;
    }

    goto LookupSidsInLocalDomainsFinish;
}


NTSTATUS
LsapDbLookupSidsInLocalDomain(
    IN ULONG LocalDomain,
    IN ULONG Count,
    IN PLSAPR_SID *Sids,
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount
    )

/*++

Routine Description:

    This function looks up Sids in a SAM domain on the local system
    attempts to translate them to names.

Arguments:

    LocalDomain - Indicates which local domain to look in.  Valid values
        are:
                LSAP_DB_SEARCH_BUILT_IN_DOMAIN
                LSAP_DB_SEARCH_ACCOUNT_DOMAIN

    Count - Number of Sids in the Sids array,  Note that some of these
        may already have been mapped elsewhere, as specified by the
        MappedCount parameter.

    Sids - Pointer to array of pointers to Sids to be translated.
        Zero or all of the Sids may already have been translated
        elsewhere.  If any of the Sids have been translated, the
        Names parameter will point to a location containing a non-NULL
        array of Name translation structures corresponding to the
        Sids.  If the nth Sid has been translated, the nth name
        translation structure will contain either a non-NULL name
        or a non-negative offset into the Referenced Domain List.  If
        the nth Sid has not yet been translated, the nth name
        translation structure will contain a zero-length name string
        and a negative value for the Referenced Domain List index.

    TrustInformation - Pointer to Trust Information specifying a Domain Sid
        and Name.

    ReferencedDomains - Pointer to a structure in which the list of domains
        used in the translation is maintained.  The entries in this structure
        are referenced by the structure returned via the Sids parameter.
        Unlike the Sids parameter, which contains an array entry for each
        translated name, this structure will only contain one component for
        each domain utilized in the translation.

    TranslatedNames - Pointer to a structure in which the translations to Names
        corresponding to the Sids specified on Sids is maintained.  The
        nth entry in this array provides a translation (where known) for the
        nth element in the Sids parameter.

    MappedCount - Pointer to location in which a count of the Names that
        have been completely translated is maintained.

    CompletelyUnmappedCount - Pointer to a location in which a count of the
        Names that have not been translated (either partially, by identification
        of a Domain Prefix, or completely) is maintained.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Sids may remain partially or completely unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.
--*/

{
    NTSTATUS
        Status,
        SecondaryStatus;

    PLSA_TRANSLATED_NAME
        OutputNames = NULL;

    SAMPR_HANDLE
        LocalSamDomainHandle = NULL;

    PLSAPR_UNICODE_STRING
        Names = NULL;

    PSID_NAME_USE
        Use = NULL;

    ULONG
        RelativeIdCount,
        RelativeIdIndex,
        SidIndex,
        LocalMappedCount = (ULONG) 0,
        DomainSidCount = (ULONG) 0;

    LONG
        DomainIndex,
        DomainSidIndexList,
        NextIndex,
        TmpIndex;

    PULONG
        RelativeIds = NULL,
        SidIndices = NULL;

    PLSAPR_SID
        DomainSid = TrustInformation->Sid;

    SAMPR_RETURNED_USTRING_ARRAY
        SamReturnedNames;

    SAMPR_ULONG_ARRAY
        SamReturnedUses;

    UCHAR
        SubAuthorityCountDomain;

    PLSAPR_TRUST_INFORMATION
        FreeTrustInformation = NULL;



    //
    // Make sure the SAM handles have been established.
    //

    Status = LsapOpenSam();
    ASSERT(NT_SUCCESS(Status));


    SamReturnedNames.Count = 0;
    SamReturnedNames.Element = NULL;
    SamReturnedUses.Count = 0;
    SamReturnedUses.Element = NULL;

    OutputNames = (PLSA_TRANSLATED_NAME) TranslatedNames->Names;

    SecondaryStatus = STATUS_SUCCESS;

    if (*MappedCount + *CompletelyUnmappedCount > Count) {
        Status = STATUS_INVALID_PARAMETER;
        goto LookupSidsInLocalDomainError;
    }

    Status = STATUS_SUCCESS;

    if (*CompletelyUnmappedCount == (ULONG) 0) {
        goto LookupSidsInLocalDomainFinish;
    }

    //
    // Now construct a list of Relative Ids to be looked up.  Any Sids that
    // do not belong to the specified domain are ignored.  Any Sids that
    // are not marked as having unknown Use are ignored, except for certain
    // Well Known Sids that do not have a Well Known Name.  These Sids
    // have known Use and a name string length of 0.
    //
    // First, scan the array of Sids looking for completely unmapped ones
    // that have the same domain prefix as the local domain we are dealing
    // with.  Note that we can omit any Sid whose Translated Name entry
    // contains a non-zero DomainIndex since the domain of that Sid has
    // already been identified.  Once the number of Sids is known, allocate
    // memory for an array of Relative Ids and a parallel array of indices
    // into the original Sids array.  The latter array will be used to locate
    // entries in the TranslatedNames array to which information returned by
    // the SamrLookupIdsInDomain() call will be copied.
    //

    for (RelativeIdCount = 0, SidIndex = 0, DomainSidIndexList = -1;
         SidIndex < Count;
         SidIndex++) {

        if ((OutputNames[SidIndex].Use == SidTypeUnknown) &&
            (OutputNames[SidIndex].DomainIndex == LSA_UNKNOWN_INDEX)) {

            if (LsapRtlPrefixSid( (PSID) DomainSid, (PSID) Sids[SidIndex])) {
                RelativeIdCount++;
            } else if (RtlEqualSid( (PSID)DomainSid, (PSID)Sids[SidIndex])) {

                //
                // This is the domain sid itself.  Update
                // the output information directly, but don't add
                // it to the list of RIDs to be looked up by SAM.
                //
                // NOTE that we don't yet know what our domain index
                // is.  So, just link these entries together and we'll
                // set the index later.
                //

                OutputNames[SidIndex].DomainIndex = DomainSidIndexList;
                DomainSidIndexList = SidIndex;
                OutputNames[SidIndex].Use         = SidTypeDomain;
                OutputNames[SidIndex].Name.Buffer = NULL;
                OutputNames[SidIndex].Name.Length = 0;
                OutputNames[SidIndex].Name.MaximumLength = 0;

                LocalMappedCount++;
                DomainSidCount++;
            }
        }
    }

    //
    // If we have any SIDs in this domain, then add it to the
    // referenced domain list.
    //

    if ((RelativeIdCount != 0) || (DomainSidCount != 0)) {

        //
        // At least one Sid has the domain Sid as prefix (or is the
        // domain SID).  Add the domain to the list of Referenced
        // Domains and obtain a Domain Index back.
        //

        Status = LsapDbLookupAddListReferencedDomains(
                     ReferencedDomains,
                     TrustInformation,
                     &DomainIndex
                     );

        if (!NT_SUCCESS(Status)) {
            goto LookupSidsInLocalDomainError;
        }

        //
        // If any of the sids were this domain's sid, then they
        // already have their OutputNames[] entry filled in except
        // that the DomainIndex was unkown.  It is now known, so
        // fill it in.  Any such entries to change have been linked
        // together using DomainSidIndexList as a listhead.
        //

        for (NextIndex = DomainSidIndexList;
             NextIndex != -1;
             NextIndex = TmpIndex ) {


            TmpIndex = OutputNames[NextIndex].DomainIndex;
            OutputNames[NextIndex].DomainIndex = DomainIndex;
        }
    }

    //
    // If any of the remaining Sids have the specified Local
    // domain Sid as prefix Sid, look them up
    //

    if (RelativeIdCount != 0) {

        //
        // Allocate memory for the Relative Id and cross reference arrays
        //

        RelativeIds = LsapAllocateLsaHeap( RelativeIdCount * sizeof(ULONG));


        Status = STATUS_INSUFFICIENT_RESOURCES;

        if (RelativeIds == NULL) {
            goto LookupSidsInLocalDomainError;
        }

        SidIndices = LsapAllocateLsaHeap( RelativeIdCount * sizeof(ULONG));

        if (SidIndices == NULL) {
            goto LookupSidsInLocalDomainError;
        }

        Status = STATUS_SUCCESS;

        //
        // Obtain the SubAuthorityCount for the Domain Sid
        //

        SubAuthorityCountDomain = *RtlSubAuthorityCountSid( (PSID) DomainSid );

        //
        // Now setup the array of Relative Ids to be looked up, recording
        // in the SidIndices array the index of the corresponding Sid within the
        // original Sids array.  Set the DomainIndex field for those Sids
        // eligible for the SAM lookup.
        //

        for (RelativeIdIndex = 0, SidIndex = 0;
             (RelativeIdIndex < RelativeIdCount) && (SidIndex < Count);
             SidIndex++) {

            if ((OutputNames[SidIndex].Use == SidTypeUnknown) &&
                (OutputNames[SidIndex].DomainIndex == LSA_UNKNOWN_INDEX)) {

                if (LsapRtlPrefixSid( (PSID) DomainSid, (PSID) Sids[SidIndex] )) {

                    SidIndices[RelativeIdIndex] = SidIndex;
                    RelativeIds[RelativeIdIndex] =
                        *RtlSubAuthoritySid(
                             (PSID) Sids[SidIndex],
                             SubAuthorityCountDomain
                             );

                    OutputNames[SidIndex].DomainIndex = DomainIndex;
                    RelativeIdIndex++;

                }
            }
        }

        //
        // Lookup the Sids in the specified SAM Domain.
        //

        if (LocalDomain == LSAP_DB_SEARCH_BUILT_IN_DOMAIN ) {
            LocalSamDomainHandle = LsapBuiltinDomainHandle;
        } else {
            ASSERT(LocalDomain == LSAP_DB_SEARCH_ACCOUNT_DOMAIN);
            LocalSamDomainHandle = LsapAccountDomainHandle;
        }

        //
        // Call SAM to lookup the Relative Id's
        //

        Status = SamrLookupIdsInDomain(
                     LocalSamDomainHandle,
                     RelativeIdCount,
                     RelativeIds,
                     &SamReturnedNames,
                     &SamReturnedUses
                     );

        if (!NT_SUCCESS(Status)) {

            //
            // The only error allowed is STATUS_NONE_MAPPED.  Filter this out.
            //

            if (Status != STATUS_NONE_MAPPED) {
                goto LookupSidsInLocalDomainError;
            }

            Status = STATUS_SUCCESS;
        }

        //
        // Now copy the information returned from SAM into the output
        // Translated Sids array.  As we go, compute a count of the names
        // mapped by SAM.
        //

        for (RelativeIdIndex = 0;
             RelativeIdIndex < SamReturnedNames.Count;
             RelativeIdIndex++) {

            SidIndex =  SidIndices[RelativeIdIndex];

            //
            // Copy the Sid Use.  If the Sid was mapped by this SAM call, copy
            // its Name and increment the count of Sids mapped by this SAM call.
            // Note that we don't need to set the DomainIndex since we did so
            // earlier.
            //

            OutputNames[SidIndex].Use = SamReturnedUses.Element[RelativeIdIndex];

            if (OutputNames[SidIndex].Use != SidTypeUnknown) {

                Status = LsapRpcCopyUnicodeString(
                             NULL,
                             &OutputNames[SidIndex].Name,
                             (PUNICODE_STRING) &SamReturnedNames.Element[RelativeIdIndex]
                             );

                if (!NT_SUCCESS(Status)) {
                    break;
                }

                LocalMappedCount++;
            }
        }

        if (!NT_SUCCESS(Status)) {

            goto LookupSidsInLocalDomainError;
        }

    }


    //
    // Update the Mapped and Completely Unmapped Counts.  To the Mapped Count
    // add in the number of Sids that SAM completely identified.  From the
    // Completely Unmapped Count subtract the number of Sids presented to
    // Sam, since all of these will be at least partially translated.
    //

    *MappedCount += LocalMappedCount;
    *CompletelyUnmappedCount -= (RelativeIdCount + DomainSidCount);

LookupSidsInLocalDomainFinish:

    //
    // If necessary, free the Lsa Heap buffer allocated for the RelativeIds
    // and SidIndices arrays.
    //

    if (RelativeIds != NULL) {

        LsapFreeLsaHeap( RelativeIds );
        RelativeIds = NULL;
    }

    if (SidIndices != NULL) {

        LsapFreeLsaHeap( SidIndices );
        SidIndices = NULL;
    }

    //
    // If necessary, free the Names array returned from SAM.
    //

    if ( SamReturnedNames.Count != 0 ) {

        SamIFree_SAMPR_RETURNED_USTRING_ARRAY ( &SamReturnedNames );
        SamReturnedNames.Count = 0;
    }

    //
    // If necessary, free the Uses array returned from SAM.
    //

    if ( SamReturnedUses.Count != 0 ) {

        SamIFree_SAMPR_ULONG_ARRAY ( &SamReturnedUses );
        SamReturnedUses.Count = 0;
    }


    return(Status);

LookupSidsInLocalDomainError:

    //
    // If necessary, free memory for the OutputTrustInformation Domain
    // Name Buffer and Sid Buffer.
    //

    if (DomainIndex >= 0) {

        FreeTrustInformation = &ReferencedDomains->Domains[DomainIndex];

        if (FreeTrustInformation->Sid != NULL) {

            MIDL_user_free( FreeTrustInformation->Sid );
            FreeTrustInformation->Sid = NULL;
        }

        if (FreeTrustInformation->Name.Buffer != NULL) {

            MIDL_user_free( FreeTrustInformation->Name.Buffer );
            FreeTrustInformation->Name.Buffer = NULL;
            FreeTrustInformation->Name.Length = 0;
            FreeTrustInformation->Name.MaximumLength = 0;
        }
    }

    //
    // If necessary, free the Name buffer in each of the OutputNames entries
    // written by this routine and set the Name slot to NULL.
    //

    for (RelativeIdIndex = 0;
         RelativeIdIndex < SamReturnedNames.Count;
         RelativeIdIndex++) {

        SidIndex =  SidIndices[RelativeIdIndex];

        if (OutputNames[SidIndex].Name.Buffer != NULL) {

            MIDL_user_free( OutputNames[SidIndex].Name.Buffer );
            OutputNames[SidIndex].Name.Buffer = NULL;
            OutputNames[SidIndex].Name.Length = 0;
            OutputNames[SidIndex].Name.MaximumLength = 0;
        }
    }

    //
    // Restore the Use field for each entry we wrote to back to SidType
    // Unknown.
    //

    for (RelativeIdIndex = 0;
         RelativeIdIndex < SamReturnedNames.Count;
         RelativeIdIndex++) {

        SidIndex =  SidIndices[RelativeIdIndex];

        if (OutputNames[SidIndex].Name.Buffer != NULL) {

            MIDL_user_free( OutputNames[SidIndex].Name.Buffer );
            OutputNames[SidIndex].Name.Buffer = NULL;
            OutputNames[SidIndex].Name.Length = 0;
            OutputNames[SidIndex].Name.MaximumLength = 0;
        }

        OutputNames[SidIndex].Use = SidTypeUnknown;
        OutputNames[SidIndex].DomainIndex = LSA_UNKNOWN_INDEX;
    }

    if (NT_SUCCESS(Status)) {

        Status = SecondaryStatus;
    }

    goto LookupSidsInLocalDomainFinish;
}


NTSTATUS
LsapDbLookupSidsInPrimaryDomain(
    IN ULONG Count,
    IN PLSAPR_SID *Sids,
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
    IN LSAP_LOOKUP_LEVEL LookupLevel,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount
    )

/*++

Routine Description:

    This function attempts to translate Sids in a Primary Domain.  A
    Trusted Domain object must exist for the domain in the local Policy
    Database.  This object will be used to access the Domain's list of
    Controllers and one or more callouts will be made to access the LSA
    Policy Databases on these Controllers.

Arguments:

    Count - Number of Sids in the Sids array,  Note that some of these
        may already have been mapped elsewhere, as specified by the
        MappedCount parameter.

    Sids - Pointer to array of pointers to Sids to be translated.
        Zero or all of the Sids may already have been translated
        elsewhere. dd esp la If any of the Sids have been translated, the

        Names parameter will point to a location containing a non-NULL
        array of Name translation structures corresponding to the
        Sids.  If the nth Sid has been translated, the nth name
        translation structure will contain either a non-NULL name
        or a non-negative offset into the Referenced Domain List.  If
        the nth Sid has not yet been translated, the nth name
        translation structure will contain a zero-length name string
        and a negative value for the Referenced Domain List index.

    TrustInformation - Specifies the name and Sid of the Primary Domain.

    ReferencedDomains - Pointer to a structure in which the list of domains
        used in the translation is maintained.  The entries in this structure
        are referenced by the structure returned via the Sids parameter.
        Unlike the Sids parameter, which contains an array entry for each
        translated name, this structure will only contain one component for
        each domain utilized in the translation.

    TranslatedNames - Pointer to a structure in which the translations to Names
        corresponding to the Sids specified on Sids is maintained.  The
        nth entry in this array provides a translation (where known) for the
        nth element in the Sids parameter.

    LookupLevel - Specifies the Level of Lookup to be performed on this
        machine.  Values of this field are are follows:

        LsapLookupPDC - Second Level Lookup performed on a Primary Domain
            Controller.  The lookup searches the Account Domain of the
            SAM Database on the controller.  If not all Sids or Names are
            found, the Trusted Domain List (TDL) is obtained from the
            LSA's Policy Database and Third Level lookups are performed
            via "handoff" to each Trusted Domain in the List.

        LsapLookupTDL - Third Level Lookup performed on a controller
            for a Trusted Domain.  The lookup searches the Account Domain of
            the SAM Database on the controller only.

        NOTE:  LsapLookupWksta is not valid for this parameter.

    MappedCount - Pointer to location in which a count of the Names that
        have been completely translated is maintained.

    CompletelyUnmappedCount - Pointer to a location in which a count of the
        Names that have not been translated (either partially, by identification
        of a Domain Prefix, or completely) is maintained.


Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Sids may remain partially or completely unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    NTSTATUS SecondaryStatus = STATUS_SUCCESS;
    LSA_HANDLE ControllerPolicyHandle = NULL;
    PLSAPR_UNICODE_STRING ControllerNames = NULL;
    ULONG NextLevelCount;
    ULONG NextLevelMappedCount;
    ULONG SidIndex;
    ULONG NextLevelSidIndex;
    PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
    PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
    PLSA_TRANSLATED_NAME NextLevelNames = NULL;
    PLSAPR_SID *NextLevelSids = NULL;
    LONG FirstEntryIndex;
    PULONG SidIndices = NULL;
    BOOLEAN PartialSidTranslationsAttempted = FALSE;

    //
    // If there are no completely unmapped Sids remaining, just return.
    //

    if (*CompletelyUnmappedCount == (ULONG) 0) {

        goto LookupSidsInPrimaryDomainFinish;
    }

    //
    // Open the Policy object on some DC for the Primary Domain.
    //

    Status = LsapDbOpenPolicyTrustedDomain(
                 TrustInformation,
                 POLICY_VIEW_LOCAL_INFORMATION,
                 &ControllerPolicyHandle
                 );

    if (!NT_SUCCESS(Status)) {

        //
        // We cannot access the Primary Domain. Suppress the error
        // and translate Domain Prefix Sids for Sids belonging to
        // the Primary Domain.
        //

        Status = STATUS_SUCCESS;
        goto LookupSidsInPrimaryDomainFinish;
    }

    //
    // We have successfully opened a Domain Controller's Policy
    // Database.  Now prepare to hand off a Sid lookup for the
    // remaining unmapped Sids to that Controller.  Here, this
    // server side of the LSA is a client of the LSA on the
    // target controller.  We will construct an array of the
    // remianing unmapped Sids, look them up and then merge the
    // resulting ReferencedDomains and Translated Names into
    // our existing list.
    //

    NextLevelCount = *CompletelyUnmappedCount;

    //
    // Allocate an array to hold the indices of unmapped Sids
    // relative to the original Sids and TranslatedNames->Names
    // arrays.
    //

    SidIndices = MIDL_user_allocate(NextLevelCount * sizeof(ULONG));

    Status = STATUS_INSUFFICIENT_RESOURCES;

    if (SidIndices == NULL) {

        goto LookupSidsInPrimaryDomainError;
    }

    //
    // Allocate an array for the Sids to be looked up at the Domain
    // Controller.
    //

    NextLevelSids = MIDL_user_allocate( sizeof(PSID) * NextLevelCount );

    if (NextLevelSids == NULL) {

        goto LookupSidsInPrimaryDomainError;
    }

    Status = STATUS_SUCCESS;

    //
    // Now scan the original array of Names and its parallel
    // Translated Sids array.  Copy over any Sids that are
    // completely unmapped.
    //

    NextLevelSidIndex = (ULONG) 0;

    for (SidIndex = 0;
         SidIndex < Count && NextLevelSidIndex < NextLevelCount;
         SidIndex++) {

        if (LsapDbCompletelyUnmappedName(&TranslatedNames->Names[SidIndex])) {

            NextLevelSids[NextLevelSidIndex] = Sids[SidIndex];
            SidIndices[NextLevelSidIndex] = SidIndex;
            NextLevelSidIndex++;
        }
    }

    NextLevelMappedCount = (ULONG) 0;

    Status = LsaICLookupSids(
                 ControllerPolicyHandle,
                 NextLevelCount,
                 (PSID *) NextLevelSids,
                 (PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
                 &NextLevelNames,
                 LookupLevel,
                 &NextLevelMappedCount
                 );

    //
    // If the callout to LsaLookupSids() was unsuccessful, disregard
    // the error and set the domain name for any Sids having this
    // domain Sid as prefix sid.  We still want to return translations
    // of Sids we have so far even if we are unable to callout to another
    // LSA.
    //

    if (!NT_SUCCESS(Status)) {

        Status = STATUS_SUCCESS;
        goto LookupSidsInPrimaryDomainFinish;
    }

    //
    // The callout to LsaLookupSids() was successful.  We now have
    // an additional list of Referenced Domains containing the
    // Primary Domain and/or one or more of its Trusted Domains.
    // Merge the two Referenced Domain Lists together, noting that
    // since they are disjoint, the second list is simply
    // concatenated with the first.  The index of the first entry
    // of the second list will be used to adjust all of the
    // Domain Index entries in the Translated Names entries.
    // Note that since the memory for the graph of the first
    // Referenced Domain list has been allocated as individual
    // nodes, we specify that the nodes in this graph can be
    // referenced by the output Referenced Domain list.
    //

    Status = LsapDbLookupMergeDisjointReferencedDomains(
                 ReferencedDomains,
                 NextLevelReferencedDomains,
                 &OutputReferencedDomains,
                 LSAP_DB_USE_FIRST_MERGAND_GRAPH
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsInPrimaryDomainError;
    }

    FirstEntryIndex = ReferencedDomains->Entries;

    //
    // Now update the original list of Translated Names.  We
    // update each entry that has newly been translated by copying
    // the entry from the new list and adjusting its Referenced
    // Domain List Index upwards by adding the index of the first
    // entry in the Next level List..
    //

    for( NextLevelSidIndex = 0;
         NextLevelSidIndex < NextLevelCount;
         NextLevelSidIndex++ ) {

        if (!LsapDbCompletelyUnmappedName(&NextLevelNames[NextLevelSidIndex])) {

            if (NextLevelNames[NextLevelSidIndex].Use != SidTypeUnknown) {

                SidIndex = SidIndices[NextLevelSidIndex];

                TranslatedNames->Names[SidIndex].Use
                = NextLevelNames[NextLevelSidIndex].Use;

                Status = LsapRpcCopyUnicodeString(
                             NULL,
                             (PUNICODE_STRING) &TranslatedNames->Names[SidIndex].Name,
                             &NextLevelNames[NextLevelSidIndex].Name
                             );

                if (!NT_SUCCESS(Status)) {

                    break;
                }
            }

            TranslatedNames->Names[SidIndex].DomainIndex =
                FirstEntryIndex +
                NextLevelNames[NextLevelSidIndex].DomainIndex;

            //
            // Update the count of completely unmapped Sids.
            //

            (*CompletelyUnmappedCount)--;
        }
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsInPrimaryDomainError;
    }

    //
    // Update the Referenced Domain List if a new one was produced
    // from the merge.  We retain the original top-level structure.
    //

    if (OutputReferencedDomains != NULL) {

        if (ReferencedDomains->Domains != NULL) {

            MIDL_user_free( ReferencedDomains->Domains );
            ReferencedDomains->Domains = NULL;
        }

        *ReferencedDomains = *OutputReferencedDomains;
        MIDL_user_free( OutputReferencedDomains );
        OutputReferencedDomains = NULL;
    }

    //
    // Update the Mapped Count and close the Controller Policy
    // Handle.
    //

    *MappedCount += NextLevelMappedCount;
    SecondaryStatus = LsaClose( ControllerPolicyHandle );
    ControllerPolicyHandle = NULL;

LookupSidsInPrimaryDomainFinish:

    //
    // We can return partial translations for Sids that have the specified
    // Domain Sid as Prefix Sid.  We do this provided there has been
    // no reported error.  Errors resulting from callout to another
    // LSA will have been suppressed.
    //

    if (NT_SUCCESS(Status) &&
        (*MappedCount < Count) &&
        !PartialSidTranslationsAttempted) {


        SecondaryStatus = LsapDbLookupTranslateUnknownSidsInDomain(
                              Count,
                              Sids,
                              TrustInformation,
                              ReferencedDomains,
                              TranslatedNames,
                              LookupLevel,
                              MappedCount,
                              CompletelyUnmappedCount
                              );

        PartialSidTranslationsAttempted = TRUE;

        if (!NT_SUCCESS(SecondaryStatus)) {

            goto LookupSidsInPrimaryDomainError;
        }
    }

    //
    // If necessary, free the Next Level Referenced Domain List.
    // Note that this structure is allocated(all_nodes) since it was
    // allocated by the client side of the Domain Controller LSA.
    //

    if (NextLevelReferencedDomains != NULL) {

        MIDL_user_free( NextLevelReferencedDomains );
        NextLevelReferencedDomains = NULL;
    }

    //
    // If necessary, free the Next Level Sids array.  We only free the
    // top level.
    //

    if (NextLevelSids != NULL) {

        MIDL_user_free( NextLevelSids );
        NextLevelSids = NULL;
    }

    //
    // If necessary, free the Next Level Translated Names array.
    // Note that this array is allocated(all_nodes).
    //

    if (NextLevelNames != NULL) {

        MIDL_user_free( NextLevelNames );
        NextLevelNames = NULL;
    }

    //
    // If necessary, free the array that maps Sid Indices from the
    // Next Level to the Current Level.
    //

    if (SidIndices != NULL) {

        MIDL_user_free( SidIndices );
        SidIndices = NULL;
    }

    //
    // If necessary, close the Controller Policy Handle.
    //

    if ( ControllerPolicyHandle != NULL) {

        SecondaryStatus = LsaClose( ControllerPolicyHandle );
        ControllerPolicyHandle = NULL;

        if (!NT_SUCCESS(SecondaryStatus)) {

            goto LookupSidsInPrimaryDomainError;
        }
    }

    return(Status);

LookupSidsInPrimaryDomainError:

    //
    // If the primary status was a success code, but the secondary
    // status was an error, propagate the secondary status.
    //

    if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {

        Status = SecondaryStatus;
    }

    goto LookupSidsInPrimaryDomainFinish;
}


NTSTATUS
LsapDbLookupSidsInTrustedDomains(
    IN ULONG Count,
    IN PLSAPR_SID *Sids,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
    IN LSAP_LOOKUP_LEVEL LookupLevel,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount
    )

/*++

Routine Description:

    This function attempts to lookup Sids to see if they belong to
    any of the Domains that are trusted by the Domain for which this
    machine is a DC.

Arguments:

    Sids - Pointer to an array of Sids to be looked up.

    Count - Number of Sids in the Sids array,  Note that some of these
        may already have been mapped elsewhere, as specified by the
        MappedCount parameter.

    ReferencedDomains - Pointer to a Referenced Domain List structure.
        The structure references an array of zero or more Trust Information
        entries, one per referenced domain.  This array will be appended to
        or reallocated if necessary.

    TranslatedNames - Pointer to structure that optionally references a list
        of name translations for some of the Sids in the Sids array.

    LookupLevel - Specifies the Level of Lookup to be performed on this
        machine.  Values of this field are are follows:

        LsapLookupPDC - Second Level Lookup performed on a Primary Domain
            Controller.  The lookup searches the Account Domain of the
            SAM Database on the controller.  If not all Sids or Names are
            found, the Trusted Domain List (TDL) is obtained from the
            LSA's Policy Database and Third Level lookups are performed
            via "handoff" to each Trusted Domain in the List.

        LsapLookupTDL - Third Level Lookup performed on a controller
            for a Trusted Domain.  The lookup searches the Account Domain of
            the SAM Database on the controller only.

        NOTE:  LsapLookupWksta is not valid for this parameter.

    MappedCount - Pointer to location containing the number of Sids
        in the Sids array that have already been mapped.  This number
        will be updated to reflect additional mapping done by this
        routine.

    CompletelyUnmappedCount - Pointer to location containing the
        count of completely unmapped Sids.  A Sid is completely unmapped
        if it is unknown and also its Domain Prefix Sid is not recognized.
        This count is updated on exit, the number of completely unmapped
        Sids whose Domain Prefices are identified by this routine being
        subtracted from the input value.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that
            some or all of the Sids may remain unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;

    //
    // Build a WorkList for this Lookup and put it on the Work Queue.
    //
    // NOTE: This routine does not need to hold the Lookup Work Queue
    //       lock to ensure validity of the WorkList pointer, because the
    //       pointer remains valid until this routine frees it via
    //       LsapDbLookupDeleteWorkList().  Although other threads may
    //       process the WorkList, do not delete it.
    //
    //       A called routine must acquire the lock in order to access
    //       the WorkList after it has been added to the Work Queue.
    //

    Status = LsapDbLookupSidsBuildWorkList(
                 Count,
                 Sids,
                 ReferencedDomains,
                 TranslatedNames,
                 LookupLevel,
                 MappedCount,
                 CompletelyUnmappedCount,
                 &WorkList
                 );

    if (!NT_SUCCESS(Status)) {

        //
        // If no Work List has been built because there are no
        // eligible domains to search, exit, suppressing the error.

        if (Status == STATUS_NONE_MAPPED) {

            Status = STATUS_SUCCESS;
            goto LookupSidsInTrustedDomainsFinish;
        }

        goto LookupSidsInTrustedDomainsError;
    }

    //
    // Start the work, by dispatching one or more worker threads
    // if necessary.
    //

    Status = LsapDbLookupDispatchWorkerThreads( WorkList );

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsInTrustedDomainsError;
    }

    //
    // Wait for completion/termination of all items on the Work List.
    //

    Status = LsapDbLookupAwaitCompletionWorkList( WorkList );

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsInTrustedDomainsError;
    }

LookupSidsInTrustedDomainsFinish:

    //
    // If a Work List was created, delete it from the Work Queue
    //

    if (WorkList != NULL) {

        Status = LsapDbLookupDeleteWorkList( WorkList );
        WorkList = NULL;
    }

    return(Status);

LookupSidsInTrustedDomainsError:

    goto LookupSidsInTrustedDomainsFinish;
}


NTSTATUS
LsapDbLookupNamesInLocalDomains(
    IN ULONG Count,
    IN PLSAPR_UNICODE_STRING Names,
    IN PLSAPR_UNICODE_STRING PrefixNames,
    IN PLSAPR_UNICODE_STRING SuffixNames,
    IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
    IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount,
    IN ULONG Options
    )

/*++

Routine Description:

    This function looks up Names in the local SAM domains and attempts to
    translate them to Sids.  Currently, there are two local SAM domains,
    the Built-in domain (which has a well-known Sid and name) and the
    Account Domain (which has a configurable Sid and name).

Arguments:

    Count - Number of Names in the Names array,  Note that some of these
        may already have been mapped elsewhere, as specified by the
        MappedCount parameter.

    Names - Pointer to array of Unicode Names to be translated.
        Zero or all of the Names may already have been translated
        elsewhere.  If any of the Names have been translated, the
        TranslatedSids parameter will point to a location containing a non-NULL
        array of Sid translation structures corresponding to the
        Names.  If the nth Name has been translated, the nth Sid
        translation structure will contain either a non-NULL RelativeId
        or a non-negative offset into the Referenced Domain List.  If
        the nth Name has not yet been translated, the nth Sid
        translation structure will contain SidTypeUnknown in its
        Use field.

    PrefixNames - Pointer to an array of Count Unicode String structures
        containing the Prefix portions of the Names.  Names having no
        Prefix are called Isolated Names.  For these, the Unicode String
        structure is set to contain a zero Length.

    SuffixNames - Pointer to an array of Count Unicode String structures
        containing the Suffix portions of the Names.

    ReferencedDomains - Pointer to a location containing either NULL
        or a pointer to a Referenced Domain List structure.  If an
        existing Referenced Domain List structure is supplied, it
        will be appended/reallocated if necessary.

    TranslatedSids - Pointer to structure that optionally references a list
        of Sid translations for some of the Names in the Names array.

    MappedCount - Pointer to location containing the number of Names
        in the Names array that have already been mapped.  This number
        will be updated to reflect additional mapping done by this
        routine.

    CompletelyUnmappedCount - Pointer to location containing the
        count of completely unmapped Sids.  A Name is completely unmapped
        if it is unknown and non-composite, or composite but with an
        unrecognized Domain component.  This count is updated on exit, the
        number of completely unmapped Namess whose Domain Prefices are
        identified by this routine being subtracted from the input value.

    Options - Specifies optional actions.

        LSAP_DB_SEARCH_BUILT_IN_DOMAIN - Search the Built In Domain

        LSAP_DB_SEARCH_ACCOUNT_DOMAIN - Search the Account Domain

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Names may remain partially or completely unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.

        STATUS_INTERNAL_DB_ERROR - A corruption has been detected in
            the LSA Database.

        STATUS_INVALID_PARAMETER - Invalid parameter

            - No handle to the Policy object was provided on a request
              to search the Account Domain.
--*/

{
    NTSTATUS
        Status,
        SecondaryStatus = STATUS_SUCCESS;

    ULONG
        UpdatedMappedCount = *MappedCount;

    LSAPR_HANDLE
        TrustedPolicyHandle = NULL;

    PLSAPR_POLICY_ACCOUNT_DOM_INFO
        PolicyAccountDomainInfo = NULL;


    //
    // If there are no completely unmapped Names remaining, return.
    //

    if (*CompletelyUnmappedCount == (ULONG) 0) {

        goto LookupNamesInLocalDomainsFinish;
    }


    //
    // If requested, lookup Names in the BUILT-IN Domain.
    //

    if (Options & LSAP_DB_SEARCH_BUILT_IN_DOMAIN) {

        Status = LsapDbLookupNamesInLocalDomain(
                     LSAP_DB_SEARCH_BUILT_IN_DOMAIN,
                     Count,
                     PrefixNames,
                     SuffixNames,
                     BuiltInDomainTrustInformation,
                     ReferencedDomains,
                     TranslatedSids,
                     &UpdatedMappedCount,
                     CompletelyUnmappedCount
                     );

        if (!NT_SUCCESS(Status)) {

            goto LookupNamesInLocalDomainsError;
        }

        //
        // If all Names are now mapped or partially mapped, finish.
        //

        if (*CompletelyUnmappedCount == (ULONG) 0) {

            goto LookupNamesInLocalDomainsFinish;
        }
    }

    //
    // If requested, search the Account Domain.
    //

    if (Options & LSAP_DB_SEARCH_ACCOUNT_DOMAIN) {

        Status = LsapDbLookupNamesInLocalDomain(
                     LSAP_DB_SEARCH_ACCOUNT_DOMAIN,
                     Count,
                     PrefixNames,
                     SuffixNames,
                     AccountDomainTrustInformation,
                     ReferencedDomains,
                     TranslatedSids,
                     &UpdatedMappedCount,
                     CompletelyUnmappedCount
                     );

        if (!NT_SUCCESS(Status)) {

            goto LookupNamesInLocalDomainsError;
        }
    }

LookupNamesInLocalDomainsFinish:


    //
    // Return the updated total count of Names mapped.
    //

    *MappedCount = UpdatedMappedCount;
    return(Status);

LookupNamesInLocalDomainsError:

    if (NT_SUCCESS(Status)) {

        Status = SecondaryStatus;
    }

    goto LookupNamesInLocalDomainsFinish;
}


NTSTATUS
LsapDbLookupNamesInLocalDomain(
    IN ULONG LocalDomain,
    IN ULONG Count,
    IN PLSAPR_UNICODE_STRING PrefixNames,
    IN PLSAPR_UNICODE_STRING SuffixNames,
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount
    )

/*++

Routine Description:

    This function looks up Names in a SAM domain on the local system and
    attempts to translate them to Sids.

Arguments:

    LocalDomain - Indicates which local domain to look in.  Valid values
        are:
                LSAP_DB_SEARCH_BUILT_IN_DOMAIN
                LSAP_DB_SEARCH_ACCOUNT_DOMAIN

    Count - Number of Names in the PrefixNames and SuffixNames arrays,
        Note that some of these may already have been mapped elsewhere, as
        specified by the MappedCount parameter.

    PrefixNames - Pointer to array of Prefix Names.  These are Domain
        Names or NULL Unicode Strings.  Only those Names whose Prefix
        Names are NULL or the same as the Domain Name specified in the
        TrustInformation parameter are eligible for the search.

    SuffixNames - Pointer to array of Terminal Names to be translated.
        Terminal Names are the last component of the name.
        Zero or all of the Names may already have been translated
        elsewhere.  If any of the Names have been translated, the
        Sids parameter will point to a location containing a non-NULL
        array of Sid translation structures corresponding to the
        Sids.  If the nth Sid has been translated, the nth Sid
        translation structure will contain either a non-NULL Sid
        or a non-negative offset into the Referenced Domain List.  If
        the nth Sid has not yet been translated, the nth name
        translation structure will contain a zero-length name string
        and a negative value for the Referenced Domain List index.

    TrustInformation - Pointer to Trust Information specifying a Domain Sid
        and Name.

    ReferencedDomains - Pointer to a Referenced Domain List.  This
        list references an array of zero or more Trust Information
        entries describing each of the domains referenced by the names.
        This array will be appended to/reallocated if necessary.

    TranslatedSids - Pointer to location containing NULL or a pointer to a
        array of Sid translation structures.

    MappedCount - Pointer to location containing the number of Names
        in the Names array that have already been mapped.  This number
        will be updated to reflect additional mapping done by this
        routine.

    CompletelyUnmappedCount - Pointer to location containing the
        count of completely unmapped Sids.  A Name is completely unmapped
        if it is unknown and non-composite, or composite but with an
        unrecognized Domain component.  This count is updated on exit, the
        number of completely unmapped Namess whose Domain Prefices are
        identified by this routine being subtracted from the input value.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Names may remain partially or completely unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.
--*/

{
    NTSTATUS
        Status = STATUS_SUCCESS,
        SecondaryStatus = STATUS_SUCCESS,
        IgnoreStatus;

    ULONG
        UnmappedNameCount = 0,
        OutputSidsLength,
        NameLookupCount,
        NameLookupIndex,
        SidIndex;

    LONG
        DomainIndex = LSA_UNKNOWN_INDEX;

    PULONG
        SidIndices = NULL;

    PLSA_TRANSLATED_SID
        OutputSids = NULL;

    SAMPR_HANDLE
        LocalSamDomainHandle = NULL,
        LocalSamUserHandle = NULL;

    PLSAPR_UNICODE_STRING
        SamLookupSuffixNames = NULL,
        DomainName = &TrustInformation->Name;

    PLSAPR_SID
        DomainSid = TrustInformation->Sid;

    SAMPR_ULONG_ARRAY
        SamReturnedIds,
        SamReturnedUses;

    PLSAPR_TRUST_INFORMATION
        FreeTrustInformation = NULL;

    PUSER_CONTROL_INFORMATION
        UserControlInfo;

    SamReturnedIds.Count = 0;
    SamReturnedIds.Element = NULL;
    SamReturnedUses.Count = 0;
    SamReturnedUses.Element = NULL;


    //
    // Make sure the SAM handles have been established.
    //

    Status = LsapOpenSam();
    ASSERT(NT_SUCCESS(Status));


    //
    // It is an internal error if the TranslatedSids or ReferencedDomains
    // parameters have not been specified.  Further, The TranslatedSids->Sids
    // pointer must be non-NULL.
    //

    ASSERT(ARGUMENT_PRESENT(TranslatedSids));
    ASSERT(TranslatedSids->Sids != NULL);
    ASSERT(ARGUMENT_PRESENT(ReferencedDomains));

    //
    // Validate the Count and MappedCount parameters.
    //


    if (*MappedCount + *CompletelyUnmappedCount > Count) {

        Status = STATUS_INVALID_PARAMETER;
        goto LookupNamesInLocalDomainError;
    }


    if (*CompletelyUnmappedCount == (ULONG) 0) {

        goto LookupNamesInLocalDomainFinish;
    }

    //
    // Not all of the Names have yet been mapped.  We'll try to map the
    // remaining names by searching the designated SAM domain on this
    // machine.
    //

    UnmappedNameCount = Count - *MappedCount;
    OutputSids = (PLSA_TRANSLATED_SID) TranslatedSids->Sids;
    OutputSidsLength = Count * sizeof (LSA_TRANSLATED_SID);

    //
    // Allocate memory for array of names to be presented to SAM.  Note
    // that, for simplicity, we allocate an array for the maximal case
    // in which all of the reamining unmapped names are eligible
    // for the search in this domain.  This avoids having to scan the
    // names array twice, once to compute the number of eligible names.
    //

    SamLookupSuffixNames = LsapAllocateLsaHeap( UnmappedNameCount * sizeof(UNICODE_STRING));


    if (SamLookupSuffixNames == NULL) {

        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto LookupNamesInLocalDomainError;
    }

    //
    // Allocate an array to record indices of eligible names.  This is
    // used upon return from SAM to locate the entries in the
    // OutputSids array that are to be updated.  For simplicity, we
    // allocate the array with sufficient entries for all of the remaining
    // unmapped names.
    //

    SidIndices = LsapAllocateLsaHeap( UnmappedNameCount * sizeof(ULONG));

    if (SidIndices == NULL) {

        goto LookupNamesInLocalDomainError;
    }


    //
    // Scan the output array of Sid translations to locate entries for names
    // that have not yet been mapped.  For each unmapped name, check
    // eligibility of the name for the search of this domain.
    //
    // - All isolated names are eligible for the search
    // - All composite names having this domain name as prefix are
    //   eligible.
    //
    // Copy in each eligible suffix or isolated name to the SAM buffer.
    //

    for (NameLookupIndex = 0, SidIndex = 0; SidIndex < Count; SidIndex++) {

        if (OutputSids[SidIndex].Use == SidTypeUnknown) {

            //
            // Found a name that has not yet been mapped.  Check for a name
            // Prefix.  If none has been specified, the whole name may either
            // be NULL, the name of the domain itself or an isolated name.
            //

            if (PrefixNames[SidIndex].Length == 0) {

                //
                // Name is isolated.  If whole name is NULL, skip.
                //

                if (SuffixNames[SidIndex].Length == 0) {

                    continue;
                }

                //
                // If name is the name of the domain itself, use the
                // Trust Information to translate it, and fill in the
                // appropriate Translated Sids entry.  The name will
                // then be excluded from further searches.
                //

                if (RtlEqualDomainName(
                        (PUNICODE_STRING) &(TrustInformation->Name),
                        (PUNICODE_STRING) &SuffixNames[SidIndex]
                        )) {


                    Status = LsapDbLookupTranslateNameDomain(
                                 TrustInformation,
                                 &OutputSids[SidIndex],
                                 ReferencedDomains,
                                 &DomainIndex
                                 );

                    if (!NT_SUCCESS(Status)) {

                        break;
                    }

                    continue;
                }

                //
                // Name is an isolated name not equal to the domain name and
                // so is eligible for the search.  Reference it from the SAM buffer,
                // remember its index and increment the buffer index.  The
                // SAM buffer is an IN parameter to a further lookup call.
                //

                SamLookupSuffixNames[NameLookupIndex] = SuffixNames[SidIndex];


                //
                // We should never have an index that equals or exceeds the
                // UnmappedNameCount.
                //

                ASSERT(NameLookupIndex < UnmappedNameCount);

                SidIndices[NameLookupIndex] = SidIndex;
                NameLookupIndex++;
                continue;
            }

            //
            // Name has a non-NULL Prefix Name.  Cpmpare the name with the
            // name of the Domain being searched.

            if (RtlEqualDomainName(
                    (PUNICODE_STRING) &TrustInformation->Name,
                    (PUNICODE_STRING) &PrefixNames[SidIndex]
                    )) {

                //
                // Prefix name matches the name of the Domain.  If the
                // Suffix Name is NULL, the name of the domain itself
                // has been specified (in the form <DomainName> "\").
                //

                if (SuffixNames[SidIndex].Length == 0) {

                    Status = LsapDbLookupTranslateNameDomain(
                                 TrustInformation,
                                 &OutputSids[SidIndex],
                                 ReferencedDomains,
                                 &DomainIndex
                                 );

                    if (!NT_SUCCESS(Status)) {

                        break;
                    }

                    continue;
                }

                //
                // Name is composite and the Prefix name matches the name of
                // this domain.  We will at least be able to partially translate
                // name, so add this domain to the Referenced Domain List if not
                // already there.  Then reference the Suffix Name from the SAM buffer,
                // remember its index and increment the buffer index. The
                // SAM buffer is an IN parameter to a further lookup call.
                //

                if (DomainIndex == LSA_UNKNOWN_INDEX) {

                    Status = LsapDbLookupAddListReferencedDomains(
                                 ReferencedDomains,
                                 TrustInformation,
                                 &DomainIndex
                                 );

                    if (!NT_SUCCESS(Status)) {

                        break;
                    }
                }

                SamLookupSuffixNames[NameLookupIndex] = SuffixNames[SidIndex];

                SidIndices[NameLookupIndex] = SidIndex;
                OutputSids[SidIndex].DomainIndex = DomainIndex;
                NameLookupIndex++;
            }
        }
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesInLocalDomainError;
    }

    //
    // If none of the remaining Names are eligible for searching in this
    // domain, finish up.
    //

    NameLookupCount = NameLookupIndex;

    if (NameLookupCount == 0) {

        goto LookupNamesInLocalDomainFinish;
    }

    //
    //
    // Lookup the Sids in the specified SAM Domain.
    //

    if (LocalDomain == LSAP_DB_SEARCH_BUILT_IN_DOMAIN ) {
        LocalSamDomainHandle = LsapBuiltinDomainHandle;
    } else {
        ASSERT(LocalDomain == LSAP_DB_SEARCH_ACCOUNT_DOMAIN);
        LocalSamDomainHandle = LsapAccountDomainHandle;
    }

    //
    // Call SAM to lookup the Names in the Domain.
    //

    Status = SamrLookupNamesInDomain(
                 LocalSamDomainHandle,
                 NameLookupCount,
                 (PRPC_UNICODE_STRING) SamLookupSuffixNames,
                 &SamReturnedIds,
                 &SamReturnedUses
                 );

    if (!NT_SUCCESS(Status)) {

        //
        // The only error allowed is STATUS_NONE_MAPPED.  Filter this out.
        //

        if (Status != STATUS_NONE_MAPPED) {

            goto LookupNamesInLocalDomainError;
        }

        Status = STATUS_SUCCESS;
        goto LookupNamesInLocalDomainFinish;
    }

    //
    // Filter through the returned Ids to eliminate user accounts that are
    // not marked NORMAL.
    //

    for (NameLookupIndex = 0;
         NameLookupIndex < SamReturnedIds.Count;
         NameLookupIndex++) {

        //
        // If this account is a User Account, check its User Control
        // Information.  If the account control information indicates
        // that the account is not a Normal User Account, for example
        // it is a machine account, do not return information about
        // the account.  Mark it as unknown.
        //

        if (SamReturnedUses.Element[ NameLookupIndex ] !=  SidTypeUser) {

            continue;
        }

        Status = SamrOpenUser(
                     LocalSamDomainHandle,
                     USER_READ_ACCOUNT,
                     SamReturnedIds.Element[ NameLookupIndex ],
                     &LocalSamUserHandle
                     );

        if (!NT_SUCCESS(Status)) {

            break;
        }

        UserControlInfo = NULL;

        Status = SamrQueryInformationUser(
                     LocalSamUserHandle,
                     UserControlInformation,
                     (PSAMPR_USER_INFO_BUFFER *) &UserControlInfo
                     );
        IgnoreStatus = SamrCloseHandle(&LocalSamUserHandle);
        ASSERT(NT_SUCCESS(IgnoreStatus));
        LocalSamUserHandle = NULL;


        if (!NT_SUCCESS(Status)) {
            MIDL_user_free( UserControlInfo );
            break;
        }

        if (!(UserControlInfo->UserAccountControl & USER_NORMAL_ACCOUNT) &&
            !(UserControlInfo->UserAccountControl & USER_TEMP_DUPLICATE_ACCOUNT)) {
            SamReturnedUses.Element[NameLookupIndex] = SidTypeUnknown;
        }

        MIDL_user_free( UserControlInfo );
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesInLocalDomainError;
    }

    //
    // SAM found at least one eligible name in the specified domain.
    // Add the domain to the Referenced Domain List.
    //

    Status = LsapDbLookupTranslateNameDomain(
                 TrustInformation,
                 NULL,
                 ReferencedDomains,
                 &DomainIndex
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesInLocalDomainError;
    }

    //
    // Now copy the information returned from SAM into the output
    // Translated Sids array.
    //

    for (NameLookupIndex = 0;
         NameLookupIndex < SamReturnedIds.Count;
         NameLookupIndex++) {

        SidIndex =  SidIndices[NameLookupIndex];

        //
        // If we have newly translated a Name, increment the mapped
        // count and copy information returned by SAM.
        //

        if ((OutputSids[SidIndex].Use == SidTypeUnknown) &&
            SamReturnedUses.Element[NameLookupIndex] != (ULONG) SidTypeUnknown) {

            (*MappedCount)++;
            OutputSids[SidIndex].Use = (SID_NAME_USE) SamReturnedUses.Element[NameLookupIndex];
            OutputSids[SidIndex].RelativeId = SamReturnedIds.Element[NameLookupIndex];
            OutputSids[SidIndex].DomainIndex = DomainIndex;
        }
    }

LookupNamesInLocalDomainFinish:

    //
    // If successful, update count of completely unmapped names provided.
    // Note that STATUS_NONE_MAPPED errors are suppressed before we get here.
    //

    if (NT_SUCCESS(Status)) {

        LsapDbUpdateCountCompUnmappedNames(TranslatedSids, CompletelyUnmappedCount);
    }

    //
    // If necessary, free the Lsa Heap buffer allocated for the
    // SamLookupSuffixNames and SidIndices arrays.
    //

    if (SamLookupSuffixNames != NULL) {
        LsapFreeLsaHeap( SamLookupSuffixNames );
    }

    if (SidIndices != NULL) {
        LsapFreeLsaHeap( SidIndices );
    }

    //
    // If necessary, free the Relative Ids array returned from SAM.
    //

    if ( SamReturnedIds.Count != 0 ) {
        SamIFree_SAMPR_ULONG_ARRAY ( &SamReturnedIds );
    }

    //
    // If necessary, free the Uses array returned from SAM.
    //

    if ( SamReturnedUses.Count != 0 ) {
        SamIFree_SAMPR_ULONG_ARRAY ( &SamReturnedUses );
    }


    return(Status);

LookupNamesInLocalDomainError:

    //
    // If necessary, free memory for the OutputTrustInformation Domain
    // Name Buffer and Sid Buffer.
    //

    if (DomainIndex >= 0) {

        FreeTrustInformation = &ReferencedDomains->Domains[DomainIndex];

        if (FreeTrustInformation->Sid != NULL) {

            MIDL_user_free( FreeTrustInformation->Sid );
            FreeTrustInformation->Sid = NULL;
        }

        if (FreeTrustInformation->Name.Buffer != NULL) {

            MIDL_user_free( FreeTrustInformation->Name.Buffer );
            FreeTrustInformation->Name.Buffer = NULL;
            FreeTrustInformation->Name.Length = 0;
            FreeTrustInformation->Name.MaximumLength = 0;
        }
    }

    //
    // Reset the Use field for each of the entries written to back to
    // SidTypeUnknown and set the DomainIndex back to LSA_UNKNOWN_INDEX.
    //

    for (NameLookupIndex = 0;
         NameLookupIndex < SamReturnedIds.Count;
         NameLookupIndex++) {

        SidIndex =  SidIndices[NameLookupIndex];
        OutputSids[SidIndex].Use = SidTypeUnknown;
        OutputSids[SidIndex].DomainIndex = LSA_UNKNOWN_INDEX;
    }


    //
    // If the last User Account handle is still open, close it..
    //

    if (LocalSamUserHandle != NULL) {
        SecondaryStatus = SamrCloseHandle(&LocalSamUserHandle);
        LocalSamUserHandle = NULL;
    }

    goto LookupNamesInLocalDomainFinish;
}


NTSTATUS
LsapDbLookupNamesInPrimaryDomain(
    IN ULONG Count,
    IN PLSAPR_UNICODE_STRING Names,
    IN PLSAPR_UNICODE_STRING PrefixNames,
    IN PLSAPR_UNICODE_STRING SuffixNames,
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN LSAP_LOOKUP_LEVEL LookupLevel,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount
    )

/*++

Routine Description:

    This function attempts to translate Names by searching the Primary
    Domain.

Arguments:

    Count - Number of Names in the Names array,  Note that some of these
        may already have been mapped elsewhere, as specified by the
        MappedCount parameter.

    Names - Pointer to array of Names to be translated.
        Zero or all of the Names may already have been translated
        elsewhere.  If any of the Names have been translated, the
        TranslatedSids parameter will point to a location containing a non-NULL
        array of Sid translation structures corresponding to the
        Names.  If the nth Name has been translated, the nth Sid
        translation structure will contain either a non-NULL Sid
        or a non-negative offset into the Referenced Domain List.  If
        the nth Name has not yet been translated, the nth Sid
        translation structure will contain a zero-length Sid string
        and a negative value for the Referenced Domain List index.

    PrefixNames - Pointer to an array of Count Unicode String structures
        containing the Prefix portions of the Names.  Names having no
        Prefix are called Isolated Names.  For these, the Unicode String
        structure is set to contain a zero Length.

    SuffixNames - Pointer to an array of Count Unicode String structures
        containing the Suffix portions of the Names.

    TrustInformation - Specifies the name and Sid of the Primary Domain.

    ReferencedDomains - Pointer to a Referenced Domain List.  This
        list references an array of zero or more Trust Information
        entries describing each of the domains referenced by the names.
        This array will be appended to/reallocated if necessary.

    TranslatedSids - Pointer to structure that optionally references a list
        of Sid translations for some of the Names in the Names array.

    LookupLevel - Specifies the Level of Lookup to be performed on this
        machine.  Values of this field are are follows:

        LsapLookupPDC - Second Level Lookup performed on a Primary Domain
            Controller.  The lookup searches the Account Domain of the
            SAM Database on the controller.  If not all Names are
            found, the Trusted Domain List (TDL) is obtained from the
            LSA's Policy Database and Third Level lookups are performed
            via "handoff" to each Trusted Domain in the List.

        LsapLookupTDL - Third Level Lookup performed on a controller
            for a Trusted Domain.  The lookup searches the Account Domain of
            the SAM Database on the controller only.

        NOTE:  LsapLookupWksta is not valid for this parameter.

    MappedCount - Pointer to location containing the number of Names
        in the Names array that have already been mapped.  This number
        will be updated to reflect additional mapping done by this
        routine.

    CompletelyUnmappedCount - Pointer to location containing the
        count of completely unmapped Names.  A Name is completely unmapped
        if it is unknown and non-composite, or composite but with an
        unrecognized Domain component.  This count is updated on exit, the
        number of completely unmapped Namess whose Domain Prefices are
        identified by this routine being subtracted from the input value.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Names may remain partially or completely unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.
--*/

{
    NTSTATUS Status;
    NTSTATUS SecondaryStatus = STATUS_SUCCESS;
    LSA_HANDLE ControllerPolicyHandle = NULL;
    ULONG NextLevelCount;
    ULONG NextLevelMappedCount;
    ULONG NameIndex;
    ULONG NextLevelNameIndex;
    PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
    PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
    PLSA_TRANSLATED_SID NextLevelSids = NULL;
    PLSAPR_UNICODE_STRING NextLevelNames = NULL;
    LONG FirstEntryIndex;
    PULONG NameIndices = NULL;
    BOOLEAN PartialNameTranslationsAttempted = FALSE;

    //
    // If there are no completely unmapped Names remaining, just return.
    //

    if (*CompletelyUnmappedCount == (ULONG) 0) {

        goto LookupNamesInPrimaryDomainFinish;
    }

    //
    // Open the Policy object on some DC for the Primary Domain.
    //

    Status = LsapDbOpenPolicyTrustedDomain(
                 TrustInformation,
                 POLICY_VIEW_LOCAL_INFORMATION,
                 &ControllerPolicyHandle
                 );

    if (!NT_SUCCESS(Status)) {

        //
        // We cannot access the Primary Domain. Suppress the error
        // and translate Domain Prefix Sids for Sids belonging to
        // the Primary Domain.
        //

        Status = STATUS_SUCCESS;
        goto LookupNamesInPrimaryDomainFinish;
    }

    //
    // We have successfully opened a Domain Controller's Policy
    // Database.  Now prepare to hand off a Name lookup for the
    // remaining unmapped Names to that Controller.  Here, this
    // server side of the LSA is a client of the LSA on the
    // target controller.  We will construct an array of the
    // remianing unmapped Names, look them up and then merge the
    // resulting ReferencedDomains and Translated Sids into
    // our existing list.
    //

    NextLevelCount = *CompletelyUnmappedCount;

    //
    // Allocate an array to hold the indices of unmapped Names
    // relative to the original Names and TranslatedSids->Sids
    // arrays.
    //

    NameIndices = MIDL_user_allocate(NextLevelCount * sizeof(ULONG));

    Status = STATUS_INSUFFICIENT_RESOURCES;

    if (NameIndices == NULL) {

        goto LookupNamesInPrimaryDomainError;
    }

    //
    // Allocate an array of UNICODE_STRING structures for the
    // names to be looked up at the Domain Controller.
    //

    NextLevelNames = MIDL_user_allocate(
                         sizeof(UNICODE_STRING) * NextLevelCount
                         );

    if (NextLevelNames == NULL) {

        goto LookupNamesInPrimaryDomainError;
    }

    Status = STATUS_SUCCESS;

    //
    // Now scan the original array of Names and its parallel
    // Translated Sids array.  Copy over any names that are completely
    // unmapped.
    //

    NextLevelNameIndex = (ULONG) 0;

    for (NameIndex = 0;
         NameIndex < Count && NextLevelNameIndex < NextLevelCount;
         NameIndex++) {

        if (LsapDbCompletelyUnmappedSid(&TranslatedSids->Sids[NameIndex])) {

            NextLevelNames[NextLevelNameIndex] = Names[NameIndex];
            NameIndices[NextLevelNameIndex] = NameIndex;
            NextLevelNameIndex++;
        }
    }

    NextLevelMappedCount = (ULONG) 0;

    Status = LsaICLookupNames(
                 ControllerPolicyHandle,
                 NextLevelCount,
                 (PUNICODE_STRING) NextLevelNames,
                 (PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
                 (PLSA_TRANSLATED_SID *) &NextLevelSids,
                 LookupLevel,
                 &NextLevelMappedCount
                 );

    //
    // If the callout to LsaLookupNames() was unsuccessful, disregard
    // the error and set the domain name for any Sids having this
    // domain Sid as prefix sid.
    //

    if (!NT_SUCCESS(Status)) {

        Status = STATUS_SUCCESS;
        goto LookupNamesInPrimaryDomainFinish;
    }

    //
    // The callout to LsaLookupNames() was successful.  We now have
    // an additional list of Referenced Domains containing the
    // Primary Domain and/or one or more of its Trusted Domains.
    // Merge the two Referenced Domain Lists together, noting that
    // since they are disjoint, the second list is simply
    // concatenated with the first.  The index of the first entry
    // of the second list will be used to adjust all of the
    // Domain Index entries in the Translated Names entries.
    // Note that since the memory for the graph of the first
    // Referenced Domain list has been allocated as individual
    // nodes, we specify that the nodes in this graph can be
    // referenced by the output Referenced Domain list.
    //

    Status = LsapDbLookupMergeDisjointReferencedDomains(
                 ReferencedDomains,
                 NextLevelReferencedDomains,
                 &OutputReferencedDomains,
                 LSAP_DB_USE_FIRST_MERGAND_GRAPH
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesInPrimaryDomainError;
    }

    FirstEntryIndex = ReferencedDomains->Entries;

    //
    // Now update the original list of Translated Names.  We
    // update each entry that has newly been translated by copying
    // the entry from the new list and adjusting its
    // Referenced Domain List Index upwards by adding the index
    // of the first entry in the Next level List..
    //

    for( NextLevelNameIndex = 0;
         NextLevelNameIndex < NextLevelCount;
         NextLevelNameIndex++ ) {

        if (!LsapDbCompletelyUnmappedSid(&NextLevelSids[NextLevelNameIndex])) {

            NameIndex = NameIndices[NextLevelNameIndex];

            TranslatedSids->Sids[NameIndex]
            = NextLevelSids[NextLevelNameIndex];

            TranslatedSids->Sids[NameIndex].DomainIndex =
                FirstEntryIndex +
                NextLevelSids[NextLevelNameIndex].DomainIndex;

            (*CompletelyUnmappedCount)--;
        }
    }

    //
    // Update the Referenced Domain List if a new one was produced
    // from the merge.  We retain the original top-level structure.
    //

    if (OutputReferencedDomains != NULL) {

        if (ReferencedDomains->Domains != NULL) {

            MIDL_user_free( ReferencedDomains->Domains );
            ReferencedDomains->Domains = NULL;
        }

        *ReferencedDomains = *OutputReferencedDomains;
        MIDL_user_free( OutputReferencedDomains );
        OutputReferencedDomains = NULL;
    }

    //
    // Update the Mapped Count and close the Controller Policy
    // Handle.
    //

    *MappedCount += NextLevelMappedCount;
    SecondaryStatus = LsaClose( ControllerPolicyHandle );
    ControllerPolicyHandle = NULL;

    //
    // Any error status that has not been suppressed must be reported
    // to the caller.  Errors such as connection failures to other LSA's
    // are suppressed.
    //

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesInPrimaryDomainError;
    }

LookupNamesInPrimaryDomainFinish:

    //
    // If necessary, update count of completely unmapped names.
    //

    if (*CompletelyUnmappedCount > (ULONG) 0) {

        LsapDbUpdateCountCompUnmappedNames(TranslatedSids, CompletelyUnmappedCount);
    }

    //
    // We can return partial translations for composite Names in which the
    // domain component is known.  We do this provided there has been
    // no reported error.  Errors resulting from callout to another
    // LSA will have been suppressed.
    //

    if (NT_SUCCESS(Status) &&
        (*MappedCount < Count) &&
        !PartialNameTranslationsAttempted) {

        SecondaryStatus = LsapDbLookupTranslateUnknownNamesInDomain(
                              Count,
                              Names,
                              PrefixNames,
                              SuffixNames,
                              TrustInformation,
                              ReferencedDomains,
                              TranslatedSids,
                              LookupLevel,
                              MappedCount,
                              CompletelyUnmappedCount
                              );

        PartialNameTranslationsAttempted = TRUE;

        if (!NT_SUCCESS(SecondaryStatus)) {

            goto LookupNamesInPrimaryDomainError;
        }
    }

    //
    // If necessary, free the Next Level Referenced Domain List.
    // Note that this structure is allocated(all_nodes) since it was
    // allocated by the client side of the Domain Controller LSA.
    //

    if (NextLevelReferencedDomains != NULL) {

        MIDL_user_free( NextLevelReferencedDomains );
        NextLevelReferencedDomains = NULL;
    }

    //
    // If necessary, free the Next Level Names array.  We only free the
    // top level, since the names therein were copied from the input
    // TranslatedNames->Names array.
    //

    if (NextLevelNames != NULL) {

        MIDL_user_free( NextLevelNames );
        NextLevelNames = NULL;
    }

    //
    // If necessary, free the Next Level Translated Sids array.  Note
    // that this array is allocated(all_nodes).
    //

    if (NextLevelSids != NULL) {

        MIDL_user_free( NextLevelSids );
        NextLevelSids = NULL;
    }

    //
    // If necessary, free the array that maps Name Indices from the
    // Next Level to the Current Level.
    //

    if (NameIndices != NULL) {

        MIDL_user_free( NameIndices );
        NameIndices = NULL;
    }

    //
    // If necessary, close the Controller Policy Handle.
    //

    if ( ControllerPolicyHandle != NULL) {

        SecondaryStatus = LsaClose( ControllerPolicyHandle );
        ControllerPolicyHandle = NULL;

        if (!NT_SUCCESS(SecondaryStatus)) {

            goto LookupNamesInPrimaryDomainError;
        }
    }

    return(Status);

LookupNamesInPrimaryDomainError:

    //
    // If the primary status was a success code, but the secondary
    // status was an error, propagate the secondary status.
    //

    if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {

        Status = SecondaryStatus;
    }

    goto LookupNamesInPrimaryDomainFinish;
}


NTSTATUS
LsapDbLookupNamesInTrustedDomains(
    IN ULONG Count,
    IN PLSAPR_UNICODE_STRING Names,
    IN PLSAPR_UNICODE_STRING PrefixNames,
    IN PLSAPR_UNICODE_STRING SuffixNames,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN LSAP_LOOKUP_LEVEL LookupLevel,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount
    )

/*++

Routine Description:

    This function attempts to lookup Names to see if they belong to
    any of the Domains that are trusted by the Domain for which this
    machine is a DC.

Arguments:

    Count - Number of Names in the Names array,  Note that some of these
        may already have been mapped elsewhere, as specified by the
        MappedCount parameter.

    Names - Pointer to array of Names to be translated.  Zero or all of the
        Names may already have been translated elsewhere.  If any of the
        Names have been translated, the TranslatedSids parameter will point
        to a location containing a non-NULL array of Sid translation
        structures corresponding to the Names.  If the nth Name has been
        translated, the nth Sid translation structure will contain either a
        non-NULL Sid or a non-negative offset into the Referenced Domain
        List.  If the nth Name has not yet been translated, the nth Sid
        translation structure will contain a zero-length Sid string and a
        negative value for the Referenced Domain List index.

    PrefixNames - Pointer to an array of Count Unicode String structures
        containing the Prefix portions of the Names.  Names having no
        Prefix are called Isolated Names.  For these, the Unicode String
        structure is set to contain a zero Length.

    SuffixNames - Pointer to an array of Count Unicode String structures
        containing the Suffix portions of the Names.

    ReferencedDomains - Pointer to a Referenced Domain List.  This
        list references an array of zero or more Trust Information
        entries describing each of the domains referenced by the names.
        This array will be appended to/reallocated if necessary.

    TranslatedSids - Pointer to structure that optionally references a list
        of Sid translations for some of the Names in the Names array.

    LookupLevel - Specifies the Level of Lookup to be performed on this
        machine.  Values of this field are are follows:

        LsapLookupPDC - Second Level Lookup performed on a Primary Domain
            Controller.  The lookup searches the Account Domain of the
            SAM Database on the controller.  If not all Names are
            found, the Trusted Domain List (TDL) is obtained from the
            LSA's Policy Database and Third Level lookups are performed
            via "handoff" to each Trusted Domain in the List.

        LsapLookupTDL - Third Level Lookup performed on a controller
            for a Trusted Domain.  The lookup searches the Account Domain of
            the SAM Database on the controller only.

        NOTE:  LsapLookupWksta is not valid for this parameter.

    MappedCount - Pointer to location containing the number of Names
        in the Names array that have already been mapped.  This number
        will be updated to reflect additional mapping done by this
        routine.

    CompletelyUnmappedCount - Pointer to location containing the
        count of completely unmapped Names.  A Name is completely unmapped
        if it is unknown and non-composite, or composite but with an
        unrecognized Domain component.  This count is updated on exit, the
        number of completely unmapped Namess whose Domain Prefices are
        identified by this routine being subtracted from the input value.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Names may remain partially or completely unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;

    //
    // Build a WorkList for this Lookup and put it on the Work Queue.
    //
    // NOTE: This routine does not need to hold the Lookup Work Queue
    //       lock to ensure validity of the WorkList pointer, because the
    //       pointer remains valid until this routine frees it via
    //       LsapDbLookupDeleteWorkList().  Although other threads may
    //       process the WorkList, do not delete it.
    //
    //       A called routine must acquire the lock in order to access
    //       the WorkList after it has been added to the Work Queue.
    //

    Status = LsapDbLookupNamesBuildWorkList(
                 Count,
                 Names,
                 PrefixNames,
                 SuffixNames,
                 ReferencedDomains,
                 TranslatedSids,
                 LookupLevel,
                 MappedCount,
                 CompletelyUnmappedCount,
                 &WorkList
                 );

    if (!NT_SUCCESS(Status)) {

        //
        // If no Work List has been built because there are no
        // eligible domains to search, exit, suppressing the error.

        if (Status == STATUS_NONE_MAPPED) {

            Status = STATUS_SUCCESS;
            goto LookupNamesInTrustedDomainsFinish;
        }

        goto LookupNamesInTrustedDomainsError;
    }

    //
    // Start the work, by dispatching one or more worker threads
    // if necessary.
    //

    Status = LsapDbLookupDispatchWorkerThreads( WorkList );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesInTrustedDomainsError;
    }

    //
    // Wait for completion/termination of all items on the Work List.
    //

    Status = LsapDbLookupAwaitCompletionWorkList( WorkList );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesInTrustedDomainsError;
    }

LookupNamesInTrustedDomainsFinish:

    //
    // If a Work List was created, delete it from the Work Queue
    //

    if (WorkList != NULL) {

        Status = LsapDbLookupDeleteWorkList( WorkList );
        WorkList = NULL;
    }

    return(Status);

LookupNamesInTrustedDomainsError:

    goto LookupNamesInTrustedDomainsFinish;
}


NTSTATUS
LsapDbLookupTranslateNameDomain(
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN OPTIONAL PLSA_TRANSLATED_SID TranslatedSid,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    OUT PLONG DomainIndex
    )

/*++

Routine Description:

    This function optionally produces a Translated Sid entry for Domain
    from its Name and Sid, and adds Trust Information for the Domain to
    a Referenced Domain List.  The index of the new (or existing) entry
    in the Referenced Domain List is returned.

Arguments:

    TrustInformation - Pointer to Trust Information for the Domain consisting
        of its Name and Sid.

    TranslatedSid - Optional pointer to Translated Sid entry which will
        be filled in with the translation for this Domain.  If NULL
        is specified, no entry is fiiled in.

    ReferencedDomains - Pointer to Referenced Domain List in which an
        entry consisting of the Trust Information for the Domain will be
        made if one does not already exist.

    DomainIndex - Receives the index of the existing or new entry for the
        Domain in the Referenced Domain List.

Return Values:

    NTSTATUS - Standard Nt Result Code.

        STATUS_SUCCESS - The call completed successfully.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.

--*/

{
    NTSTATUS Status;


    Status = LsapDbLookupAddListReferencedDomains(
                 ReferencedDomains,
                 TrustInformation,
                 DomainIndex
                 );

    if (!NT_SUCCESS(Status)) {

        goto TranslateNameDomainError;
    }

    //
    // If requested, fill in a Sid translation entry for the domain.
    //

    if (TranslatedSid != NULL) {

        TranslatedSid->Use = SidTypeDomain;
        TranslatedSid->RelativeId = LSA_UNKNOWN_ID;
        TranslatedSid->DomainIndex = *DomainIndex;
    }

TranslateNameDomainFinish:

    return(Status);

TranslateNameDomainError:

    goto TranslateNameDomainFinish;
}


NTSTATUS
LsapDbLookupTranslateUnknownSidsInDomain(
    IN ULONG Count,
    IN PLSAPR_SID *Sids,
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
    IN LSAP_LOOKUP_LEVEL LookupLevel,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount
    )

/*++

Routine Description:

    This function looks among the unknown Sids in the given list and
    translates the Domain Name for any whose Domain Prefix Sid matches
    the given Domain Sid.

Arguments:

    Count - Number of Sids in the Sids array,  Note that some of these
        may already have been mapped elsewhere, as specified by the
        MappedCount parameter.

    Sids - Pointer to array of pointers to Sids to be translated.
        Zero or all of the Sids may already have been translated
        elsewhere.  If any of the Sids have been translated, the
        Names parameter will point to a location containing a non-NULL
        array of Name translation structures corresponding to the
        Sids.  If the nth Sid has been translated, the nth name
        translation structure will contain either a non-NULL name
        or a non-negative offset into the Referenced Domain List.  If
        the nth Sid has not yet been translated, the nth name
        translation structure will contain a zero-length name string
        and a negative value for the Referenced Domain List index.

    TrustInformation - Pointer to Trust Information specifying a Domain Sid
        and Name.

    ReferencedDomains - Pointer to a Referenced Domain List structure.
        The structure references an array of zero or more Trust Information
        entries, one per referenced domain.  This array will be appended to
        or reallocated if necessary.

    TranslatedNames - Pointer to structure that optionally references a list
        of name translations for some of the Sids in the Sids array.

    MappedCount - Pointer to location containing the number of Sids
        in the Sids array that have already been mapped.  This number
        will be updated to reflect additional mapping done by this
        routine.

    CompletelyUnmappedCount - Pointer to location containing the
        count of completely unmapped Sids.  A Sid is completely unmapped
        if it is unknown and also its Domain Prefix Sid is not recognized.
        This count is updated on exit, the number of completely unmapped
        Sids whose Domain Prefices are identified by this routine being
        subtracted from the input value.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Sids may remain partially or completely unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG RemainingCompletelyUnmappedCount;
    ULONG SidIndex;
    PSID DomainSid = TrustInformation->Sid;
    BOOLEAN DomainAlreadyAdded = FALSE;
    LONG DomainIndex;

    //
    // Scan the array of Sids looking for ones whose domain has not been
    // found.
    //

    for( SidIndex = 0,
         RemainingCompletelyUnmappedCount = *CompletelyUnmappedCount;
         (RemainingCompletelyUnmappedCount > 0) && (SidIndex < Count);
         SidIndex++) {

        //
        // Check if this Sid is completely unmapped (i.e. its domain
        // has not yet been identified.
        //

        if (LsapDbCompletelyUnmappedName(&TranslatedNames->Names[SidIndex])) {

            //
            // Found a completely unmapped Sid.  If it belongs to the
            // specified Domain, add the Domain to the Referenced Domain
            // list if we have not already done so.
            //

            if (LsapRtlPrefixSid( DomainSid, (PSID) Sids[SidIndex])) {

                if (!DomainAlreadyAdded) {

                    Status = LsapDbLookupAddListReferencedDomains(
                                 ReferencedDomains,
                                 TrustInformation,
                                 &DomainIndex
                                 );

                    if (!NT_SUCCESS(Status)) {

                        break;
                    }

                    DomainAlreadyAdded = TRUE;
                }

                //
                // Reference the domain from the TranslatedNames entry
                //

                TranslatedNames->Names[SidIndex].DomainIndex = DomainIndex;

                //
                // This Sid is now partially translated, so reduce the
                // count of completely unmapped Sids.
                //

                (*CompletelyUnmappedCount)--;
            }

            //
            // Decrement count of completely unmapped Sids scanned.
            //

            RemainingCompletelyUnmappedCount--;
        }
    }

    return(Status);
}



NTSTATUS
LsapDbLookupTranslateUnknownNamesInDomain(
    IN ULONG Count,
    IN PLSAPR_UNICODE_STRING Names,
    IN PLSAPR_UNICODE_STRING PrefixNames,
    IN PLSAPR_UNICODE_STRING SuffixNames,
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN LSAP_LOOKUP_LEVEL LookupLevel,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount
    )

/*++

Routine Description:

    This function looks among the unknown Sids in the given list and
    translates the Domain Name for any whose Domain Prefix Sid matches
    the given Domain Sid.

Arguments:

    Count - Number of Names in the Names array,  Note that some of these
        may already have been mapped elsewhere, as specified by the
        MappedCount parameter.

    Names - Pointer to array of Names to be translated.
        Zero or all of the Names may already have been translated
        elsewhere.  If any of the Names have been translated, the
        TranslatedSids parameter will point to a location containing a non-NULL
        array of Sid translation structures corresponding to the
        Names.  If the nth Name has been translated, the nth Sid
        translation structure will contain either a non-NULL Sid
        or a non-negative offset into the Referenced Domain List.  If
        the nth Name has not yet been translated, the nth Sid
        translation structure will contain a zero-length Sid string
        and a negative value for the Referenced Domain List index.

    PrefixNames - Pointer to an array of Count Unicode String structures
        containing the Prefix portions of the Names.  Names having no
        Prefix are called Isolated Names.  For these, the Unicode String
        structure is set to contain a zero Length.

    SuffixNames - Pointer to an array of Count Unicode String structures
        containing the Suffix portions of the Names.

    TrustInformation - Pointer to Trust Information specifying a Domain Sid
        and Name.

    ReferencedDomains - Pointer to a Referenced Domain List.  This
        list references an array of zero or more Trust Information
        entries describing each of the domains referenced by the names.
        This array will be appended to/reallocated if necessary.

    TranslatedSids - Pointer to structure that optionally references a list
        of Sid translations for some of the Names in the Names array.

    LookupLevel - Specifies the Level of Lookup to be performed on this
        machine.  Values of this field are are follows:

        LsapLookupPDC - Second Level Lookup performed on a Primary Domain
            Controller.  The lookup searches the Account Domain of the
            SAM Database on the controller.  If not all Names are
            found, the Trusted Domain List (TDL) is obtained from the
            LSA's Policy Database and Third Level lookups are performed
            via "handoff" to each Trusted Domain in the List.

        LsapLookupTDL - Third Level Lookup performed on a controller
            for a Trusted Domain.  The lookup searches the Account Domain of
            the SAM Database on the controller only.

        NOTE:  LsapLookupWksta is not valid for this parameter.

    MappedCount - Pointer to location containing the number of Names
        in the Names array that have already been mapped.  This number
        will be updated to reflect additional mapping done by this
        routine.

    CompletelyUnmappedCount - Pointer to location containing the
        count of completely unmapped Names.  A Name is completely unmapped
        if it is unknown and non-composite, or composite but with an
        unrecognized Domain component.  This count is updated on exit, the
        number of completely unmapped Namess whose Domain Prefices are
        identified by this routine being subtracted from the input value.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.  Note that some
            or all of the Names may remain partially or completely unmapped.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
            such as memory, to complete the call.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG RemainingCompletelyUnmappedCount;
    ULONG NameIndex;
    PLSAPR_UNICODE_STRING DomainName = &TrustInformation->Name;
    BOOLEAN DomainAlreadyAdded = FALSE;
    LONG DomainIndex;

    //
    // Scan the array of Names looking for composite ones whose domain has
    // not been found.
    //

    for( NameIndex = 0,
         RemainingCompletelyUnmappedCount = *CompletelyUnmappedCount;
         (RemainingCompletelyUnmappedCount > 0) && (NameIndex < Count);
         NameIndex++) {

        //
        // Check if this Name is completely unmapped (i.e. its domain
        // has not yet been identified).
        //

        if (TranslatedSids->Sids[NameIndex].DomainIndex == LSA_UNKNOWN_INDEX) {

            //
            // Found a completely unmapped Name.  If it belongs to the
            // specified Domain, add the Domain to the Referenced Domain
            // list if we have not already done so.
            //

            if (LsapRtlPrefixName(
                    (PUNICODE_STRING) DomainName,
                    (PUNICODE_STRING) &Names[NameIndex])
                    ) {

                if (!DomainAlreadyAdded) {

                    Status = LsapDbLookupAddListReferencedDomains(
                                 ReferencedDomains,
                                 TrustInformation,
                                 &DomainIndex
                                 );

                    if (!NT_SUCCESS(Status)) {

                        break;
                    }

                    DomainAlreadyAdded = TRUE;
                }

                //
                // Reference the domain from the TranslatedNames entry
                //

                TranslatedSids->Sids[NameIndex].DomainIndex = DomainIndex;

                //
                // This name is now partially translated, so reduce the
                // count of completely unmapped names.
                //

                (*CompletelyUnmappedCount)--;
            }

            //
            // Decrement count of completely unmapped Names scanned.
            //

            RemainingCompletelyUnmappedCount--;
        }
    }

    return(Status);
}


NTSTATUS
LsapDbLookupDispatchWorkerThreads(
    IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
    )

/*++

Routine Description:

    This function dispatches sufficient worker threads to handle a
    Lookup operation.  The worker threads can handle work items
    on any Lookup's Work List, so the number of existing active
    threads is taken into account.  Note that the total number of
    active Lookup Worker Threads may exceed the guide maximum number of
    threads, in situations where an active thread terminates during
    the dispatch cycle.  This strategy saves having to recheck the
    active thread count each time a thread is dispatched.

    NOTE:  This routine expects the specified pointer to a Work List to be
           valid.  A Work List pointer always remains valid until its
           Primary thread detects completion of the Work List via this
           routine and then deletes it via LsapDbLookupDeleteWorkList().

Arguments:

    WorkList - Pointer to a Work List describing a Lookup Sid or Lookup Name
        operation.


Return Value:

    NTSTATUS - Standard Nt Result Code

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    NTSTATUS DispatchThreadStatus = STATUS_SUCCESS;
    ULONG AdvisoryChildThreadCount;
    ULONG DispatchThreadCount;
    ULONG MaximumDispatchChildThreadCount;
    ULONG ThreadNumber;
    HANDLE Thread = NULL;
    DWORD Ignore;
    BOOLEAN AcquiredWorkQueueLock = FALSE;

    //
    // Acquire the Lookup Work Queue Lock.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto LookupDispatchWorkerThreadsError;
    }

    AcquiredWorkQueueLock = TRUE;

    //
    // Calculate the number of Worker Threads to dispatch (if any).  If
    // the WorkList has an Advisory Child Thread Count of 0, we will
    // not dispatch any threads, but instead will perform this Lookup
    // within this thread.  If the WorkList has an Advisory Child Thread
    // Count > 0, we will dispatch additional threads.  The number of
    // additional child threads dispatched is given by the formula:
    //
    // ThreadsToDispatch =
    //     min (MaximumChildThreadCount - ActiveChildThreadCount,
    //          AdvisoryChildThreadCount)
    //

    AdvisoryChildThreadCount = WorkList->AdvisoryChildThreadCount;

    if (AdvisoryChildThreadCount > 0) {

        MaximumDispatchChildThreadCount =
            LookupWorkQueue.MaximumChildThreadCount -
            LookupWorkQueue.ActiveChildThreadCount;

        if (AdvisoryChildThreadCount <= MaximumDispatchChildThreadCount) {

            DispatchThreadCount = AdvisoryChildThreadCount;

        } else {

            DispatchThreadCount = MaximumDispatchChildThreadCount;
        }

        //
        // Release the Lookup Work Queue Lock
        //

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;

        //
        // Signal the event that indicates that a new Work List is initiated.
        //

        Status = NtSetEvent( LsapDbLookupStartedEvent, NULL );

        if (!NT_SUCCESS(Status)) {

            LsapDiagPrint( DB_LOOKUP_WORK_LIST,
                          ("LsapDbLookupDispatchWorkList... NtSetEvent failed 0x%lx\n",Status));
            goto LookupDispatchWorkerThreadsError;
        }

        //
        // Dispatch the computed number of threads.
        //

        for (ThreadNumber = 0; ThreadNumber < DispatchThreadCount; ThreadNumber++) {

            Thread = CreateThread(
                         NULL,
                         0L,
                         (LPTHREAD_START_ROUTINE) LsapDbLookupWorkerThreadStart,
                         NULL,
                         0L,
                         &Ignore
                         );

            if (Thread == NULL) {

                Status = GetLastError();

                KdPrint(("LsapDbLookupDispatchWorkerThreads: CreateThread failed 0x%lx\n"));

                break;

            } else {

                CloseHandle( Thread );
            }
        }

        if (!NT_SUCCESS(Status)) {

            DispatchThreadStatus = Status;
        }
    }

    //
    // Do some work in the main thread too.
    //

    LsapDbLookupWorkerThread( TRUE);

LookupDispatchWorkerThreadsFinish:

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupDispatchWorkerThreadsError:

    goto LookupDispatchWorkerThreadsFinish;
}


NTSTATUS
LsapDbOpenPolicyTrustedDomain(
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN ACCESS_MASK DesiredAccess,
    OUT PLSA_HANDLE ControllerPolicyHandle
    )

/*++

Routine Description:

    This function opens a handle to the Lsa Policy Database on a
    DC for a given Trusted Domain.  The Domain may be specified either
    by Sid or by Name.

Arguments:


    TrustInformation - Specifies the Sid and/or Name of the Trusted Domain
        whose Policy database is to be opened.

    DesiredAccess - Specifies the desired access type to be granted.

    ControllerPolicyHandle - Receives handle oto the LSA Policy Object
        for the Lsa Policy database located on some DC for the specified
        Trusted Domain.

Return Value:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.

        STATUS_NO_MORE_ENTRIES - The DC list for the specified domain
            is null.

        Result codes from called routines.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    NTSTATUS SecondaryStatus;
    LSAPR_UNICODE_STRING DCName;
    PLSAPR_UNICODE_STRING DomainName = NULL;
    PLSAPR_TRUST_INFORMATION OutputTrustInformation = NULL;
    LSAPR_HANDLE TrustedDomainHandle = NULL;
    PLSAPR_TRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
    PLSAPR_SID DomainSid = NULL;
    LSA_HANDLE OutputControllerPolicyHandle = NULL;
    ULONG ControllerIndex, ControllerCount;

    DCName.Length = DCName.MaximumLength = (USHORT) 0;
    DCName.Buffer = NULL;

    //
    // If we're given the Domain Sid only, we need to get its name.
    //

    if ((TrustInformation->Name.Length == 0) ||
        (TrustInformation->Name.Buffer == NULL)) {

        Status = STATUS_INVALID_PARAMETER;

        if (TrustInformation->Sid == NULL) {

            goto OpenPolicyTrustedDomainError;
        }

        Status = LsapDbLookupSidTrustedDomainList(
                     NULL,
                     TrustInformation->Sid,
                     &OutputTrustInformation
                     );

        if (!NT_SUCCESS(Status)) {

            goto OpenPolicyTrustedDomainError;
        }

        DomainName = &OutputTrustInformation->Name;

    } else {

        DomainName = &TrustInformation->Name;
    }

    //
    // Use netlogon services to locate a DC Name routine if possible.  Even
    // if the routine exists, it will return an error if netlogon.dll
    // is not loaded.
    //

    Status = I_NetGetAnyDCName(
                 (PUNICODE_STRING) DomainName,
                 (PUNICODE_STRING) &DCName
                 );

    if (NT_SUCCESS(Status)) {

        if (DCName.Length != 0) {

            Status = LsapRtlValidateControllerTrustedDomain(
                         &DCName,
                         TrustInformation,
                         DesiredAccess,
                         &OutputControllerPolicyHandle
                         );

            if (NT_SUCCESS(Status)) {

                goto OpenPolicyTrustedDomainFinish;
            }
        }
    }

    //
    // An error occurred trying to obtain the name of a DC via net logon
    // services.  If the the error is simply that netlogon.dll is not loaded
    // or running, fall back to the Trusted Controllers List in the LSA's
    // TrustedDomain object for the Domain.  Any other error means that
    // there is also no point using the Trusted Controllers List either.
    //

    if (!NT_SUCCESS(Status)) {

        if (!(Status == STATUS_NETLOGON_NOT_STARTED) ||
             (Status == STATUS_DLL_NOT_FOUND)) {

            goto OpenPolicyTrustedDomainError;
        }
    }

    //
    // We could not use netlogon.dll services to locate a DC only because
    // netlogon.dll was not loaded and/or initilaized.  Resort to the Trusted
    // Controllers Info held in the TrustedDomain object.  To access this
    // object we first need to get the Domain Sid if we don't have it already.
    //

    Status = STATUS_SUCCESS;

    DomainSid = TrustInformation->Sid;

    if (DomainSid == NULL) {

        Status = LsapDbLookupNameTrustedDomainList(
                     NULL,
                     &TrustInformation->Name,
                     &OutputTrustInformation
                     );

        if (!NT_SUCCESS(Status)) {

            goto OpenPolicyTrustedDomainError;
        }

        DomainSid = OutputTrustInformation->Sid;
    }

    //
    // Open the Trusted Domain object on the local machine.
    //

    Status = LsarOpenTrustedDomain(
                 LsapPolicyHandle,
                 DomainSid,
                 TRUSTED_QUERY_CONTROLLERS,
                 &TrustedDomainHandle
                 );

    if (!NT_SUCCESS(Status)) {

        if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {

            goto OpenPolicyTrustedDomainError;
        }

        goto OpenPolicyTrustedDomainFinish;
    }

    //
    // Now obtain the list of Domain Controllers for the Primary Domain.
    // Any error here is a problem with the local machine and should
    // be reported.  Note that no error is returned just because
    // the trusted controllers info has never been set.
    //

    Status = LsarQueryInfoTrustedDomain(
                 TrustedDomainHandle,
                 TrustedControllersInformation,
                 (PLSAPR_TRUSTED_DOMAIN_INFO *) &TrustedControllersInfo
                 );

    if (!NT_SUCCESS(Status)) {

        goto OpenPolicyTrustedDomainError;
    }

    Status = STATUS_NO_MORE_ENTRIES;

    ControllerCount = TrustedControllersInfo->Entries;

    if (ControllerCount == (ULONG) 0) {

        goto OpenPolicyTrustedDomainError;
    }

    //
    // Pick a valid DC from the list and open its Policy Object.
    // NOTE: This algorithm is non-optimal as the selection should
    // randomly try all the controllers.  This doesn't matter much since
    // this code is executed only as a default when CliffV's algorithm
    // is not implemented.
    //

    for (ControllerIndex = 0; ControllerIndex < ControllerCount; ControllerIndex++) {

        DCName = TrustedControllersInfo->Names[ControllerIndex];

        //
        // Validate the next DC on the list and if valid, return a handle,
        //

        Status = LsapRtlValidateControllerTrustedDomain(
                     &DCName,
                     TrustInformation,
                     DesiredAccess,
                     &OutputControllerPolicyHandle
                     );

        //
        // Intentionally ignore the error status, except for the
        // final controller tried.
        //

        if (!NT_SUCCESS(Status)) {

            continue;
        }

        break;
    }

    if (!NT_SUCCESS(Status)) {

        goto OpenPolicyTrustedDomainError;
    }

OpenPolicyTrustedDomainFinish:

    *ControllerPolicyHandle = OutputControllerPolicyHandle;

    //
    // If necessary, close the Trusted Domain object handle.
    //

    if (TrustedDomainHandle != NULL) {

        SecondaryStatus = LsarClose( &TrustedDomainHandle );

        TrustedDomainHandle = NULL;
    }

    //
    // If necessary, free memory allocated for the DCName buffer.
    //

    if (DCName.Buffer != NULL) {

        MIDL_user_free( DCName.Buffer );
    }

    //
    // If allocated, free the TrustedControllersInfo top-level structure
    //

    if (TrustedControllersInfo != NULL) {

        MIDL_user_free(TrustedControllersInfo);
    }

    return(Status);

OpenPolicyTrustedDomainError:

    goto OpenPolicyTrustedDomainFinish;
}


NTSTATUS
LsapRtlValidateControllerTrustedDomain(
    IN PLSAPR_UNICODE_STRING DomainControllerName,
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN ACCESS_MASK DesiredAccess,
    OUT PLSA_HANDLE ControllerPolicyHandle
    )

/*++

Routine Description:

    This function verifies that a specified computer is a DC for
    a specified Domain and opens the LSA Policy Object with the
    desired accesses.

Arguments:

    DomainControllerName - Pointer to Unicode String computer name.

    TrustInformation - Domain Trust Information.  Only the Sid is
        used.

    DesiredAccess - Specifies the accesses desired to the target machine's
        Lsa Policy Database.

    PolicyHandle - Receives handle to the Lsa Policy object on the target
        machine.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_OBJECT_NAME_NOT_FOUND - The specified computer is not a
        Domain Controller for the specified Domain.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    NTSTATUS SecondaryStatus = STATUS_SUCCESS;
    SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
    OBJECT_ATTRIBUTES ObjectAttributes;
    LSA_HANDLE OutputControllerPolicyHandle = NULL;
    PLSAPR_POLICY_PRIMARY_DOM_INFO PolicyPrimaryDomainInfo = NULL;
    PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;

    //
    // Open a handle to the Policy Object on the target DC.
    //

    SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
    SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
    SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
    SecurityQualityOfService.EffectiveOnly = FALSE;

    //
    // Set up the object attributes prior to opening the LSA.
    //

    InitializeObjectAttributes(
        &ObjectAttributes,
        NULL,
        0L,
        NULL,
        NULL
        );

    //
    // The InitializeObjectAttributes macro presently stores NULL for
    // the SecurityQualityOfService field, so we must manually copy that
    // structure for now.
    //

    ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;

    Status = LsaOpenPolicy(
                 (PUNICODE_STRING) DomainControllerName,
                 &ObjectAttributes,
                 POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
                 &OutputControllerPolicyHandle
                 );

    if (!NT_SUCCESS(Status)) {

        goto ValidateControllerTrustedDomainError;
    }

    //
    // We successfully opened the Policy object on the Controller's
    // Lsa.  Check that the target system is a DC for the Domain.  We
    // verify that its Primary Domain and Account Domain information
    // both specify the given domain.  This is a hack because there is
    // no equivalent of RtlGetNtProductType for remote systems.  So,
    // first compare the Primary Domain Info...
    //

    Status = LsaQueryInformationPolicy(
                 OutputControllerPolicyHandle,
                 PolicyPrimaryDomainInformation,
                 (PVOID *) &PolicyPrimaryDomainInfo
                 );

    if (!NT_SUCCESS(Status)) {

        goto ValidateControllerTrustedDomainError;
    }

    //
    // Now compare the Domain Name and Sid stored for the Controller's
    // Primary Domain with the Domain Name and Sid provided.
    //

    Status = STATUS_OBJECT_NAME_NOT_FOUND;

    if (!RtlEqualDomainName(
            (PUNICODE_STRING) &TrustInformation->Name,
            (PUNICODE_STRING) &PolicyPrimaryDomainInfo->Name
            )) {

        goto ValidateControllerTrustedDomainError;
    }

    if (!RtlEqualSid(
            (PSID) TrustInformation->Sid,
            (PSID) PolicyPrimaryDomainInfo->Sid
            )) {

        goto ValidateControllerTrustedDomainError;
    }

    //
    // Now compare the Account Domain info.
    //

    Status = LsaQueryInformationPolicy(
                 OutputControllerPolicyHandle,
                 PolicyAccountDomainInformation,
                 (PVOID *) &PolicyAccountDomainInfo
                 );

    if (!NT_SUCCESS(Status)) {

        goto ValidateControllerTrustedDomainError;
    }

    //
    // Now compare the Domain Name and Sid stored for the Controller's
    // Account Domain with the Domain Name and Sid provided.
    //

    Status = STATUS_OBJECT_NAME_NOT_FOUND;

    if (!RtlEqualDomainName(
            (PUNICODE_STRING) &TrustInformation->Name,
            (PUNICODE_STRING) &PolicyAccountDomainInfo->DomainName
            )) {

        goto ValidateControllerTrustedDomainError;
    }

    if (!RtlEqualSid(
            (PSID) TrustInformation->Sid,
            (PSID) PolicyAccountDomainInfo->DomainSid
            )) {

        goto ValidateControllerTrustedDomainError;
    }

    Status = STATUS_SUCCESS;

ValidateControllerTrustedDomainFinish:

    //
    // Free up the Policy Primary Domain Info.
    //

    if (PolicyPrimaryDomainInfo != NULL) {

        LsaFreeMemory( (PVOID) PolicyPrimaryDomainInfo );

        PolicyPrimaryDomainInfo = NULL;
    }

    //
    // Free up the Policy Account Domain Info.
    //

    if (PolicyAccountDomainInfo != NULL) {

        LsaFreeMemory( (PVOID) PolicyAccountDomainInfo );

        PolicyAccountDomainInfo = NULL;
    }

    //
    // Return Controller Policy handle or NULL.
    //

    *ControllerPolicyHandle = OutputControllerPolicyHandle;

    return(Status);

ValidateControllerTrustedDomainError:

    //
    // Close the last Controller's Policy Handle.
    //

    if (OutputControllerPolicyHandle != NULL) {

        SecondaryStatus = LsaClose( OutputControllerPolicyHandle );
        OutputControllerPolicyHandle = NULL;
    }

    goto ValidateControllerTrustedDomainFinish;
}


NTSTATUS
LsapDbLookupAcquireWorkQueueLock(
    )

/*++

Routine Description:

    This function acquires the LSA Database Lookup Sids/Names Work Queue Lock.
    This lock serializes additions or deletions of work lists to/from the
    queue, and sign-in/sign-out of work items by worker threads.

Arguments:

    None.

Return Value:

    NTSTATUS - Standard Nt Result Code.

--*/

{
    NTSTATUS Status;

    Status = RtlEnterCriticalSection(&LookupWorkQueue.Lock);

    return(Status);
}


VOID
LsapDbLookupReleaseWorkQueueLock(
    )

/*++

Routine Description:

    This function releases the LSA Database Lookup Sids/Names Work Queue Lock.

Arguments:

    None.

Return Value:

    None.  Any error occurring within this routine is an internal error.

--*/

{
    RtlLeaveCriticalSection(&LookupWorkQueue.Lock);
}


NTSTATUS
LsapDbLookupNamesBuildWorkList(
    IN ULONG Count,
    IN PLSAPR_UNICODE_STRING Names,
    IN PLSAPR_UNICODE_STRING PrefixNames,
    IN PLSAPR_UNICODE_STRING SuffixNames,
    IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN LSAP_LOOKUP_LEVEL LookupLevel,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount,
    OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
    )

/*++

Routine Description:

    This function constructs a Work List for an LsarLookupNames call.  The
    Work List contains the parameters of the call, and an array of Work Items.
    Each Work Item specifies either all of the Names to be looked up
    in a given domain.

    Qualified names (i.e. those of the form DomainName\UserName) are
    sorted into different Work Items, one for each DomainName specifed.
    Unqualified names (i.e. those of the form UserName) are added to
    every Work Item.

Arguments:

    Count - Specifies the number of names to be translated.

    Names - Pointer to an array of Count Unicode String structures
        specifying the names to be looked up and mapped to Sids.
        The strings may be names of User, Group or Alias accounts or
        domains.

    PrefixNames - Pointer to an array of Count Unicode String structures
        containing the Prefix portions of the Names.  Names having no
        Prefix are called Isolated Names.  For these, the Unicode String
        structure is set to contain a zero Length.

    SuffixNames - Pointer to an array of Count Unicode String structures
        containing the Suffix portions of the Names.

    ReferencedDomains - receives a pointer to a structure describing the
        domains used for the translation.  The entries in this structure
        are referenced by the structure returned via the Sids parameter.
        Unlike the Sids parameter, which contains an array entry for
        each translated name, this structure will only contain one
        component for each domain utilized in the translation.

        When this information is no longer needed, it must be released
        by passing the returned pointer to LsaFreeMemory().

    TranslatedSids - Pointer to a structure which will (or already) references an array of
        records describing each translated Sid.  The nth entry in this array
        provides a translation for the nth element in the Names parameter.

        When this information is no longer needed, it must be released
        by passing the returned pointer to LsaFreeMemory().

    LookupLevel - Specifies the Level of Lookup to be performed on this
        machine.  Values of this field are are follows:

        LsapLookupWksta - First Level Lookup performed on a workstation
            normally configured for Windows-Nt.   The lookup searches the
            Well-Known Sids/Names, and the Built-in Domain and Account Domain
            in the local SAM Database.  If not all Sids or Names are
            identified, performs a "handoff" of a Second level Lookup to the
            LSA running on a Controller for the workstation's Primary Domain
            (if any).

        LsapLookupPDC - Second Level Lookup performed on a Primary Domain
            Controller.  The lookup searches the Account Domain of the
            SAM Database on the controller.  If not all Sids or Names are
            found, the Trusted Domain List (TDL) is obtained from the
            LSA's Policy Database and Third Level lookups are performed
            via "handoff" to each Trusted Domain in the List.

        LsapLookupTDL - Third Level Lookup performed on a controller
            for a Trusted Domain.  The lookup searches the Account Domain of
            the SAM Database on the controller only.

        MappedCount - Pointer to location that contains a count of the Names
            mapped so far. On exit, this will be updated.

    MappedCount - Pointer to location containing the number of Names
        in the Names array that have already been mapped.  This number
        will be updated to reflect additional mapping done by this
        routine.

    CompletelyUnmappedCount - Pointer to location containing the
        count of completely unmapped Names.  A Name is completely unmapped
        if it is unknown and non-composite, or composite but with an
        unrecognized Domain component.  This count is updated on exit, the
        number of completely unmapped Namess whose Domain Prefices are
        identified by this routine being subtracted from the input value.

    WorkList - Receives pointer to completed Work List if successfully built.

Return Value:

    NTSTATUS - Standard Nt Result Code

        STATUS_NONE_MAPPED - All of the Names specified were composite,
            but none of their Domains appear in the Trusted Domain List.
            No Work List has been generated.  Note that this is not a
            fatal error.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS, IgnoreStatus = STATUS_SUCCESS;
    PLSAP_DB_LOOKUP_WORK_LIST OutputWorkList = NULL;
    ULONG NameIndex;
    PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
    PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
    PLSAP_DB_LOOKUP_WORK_ITEM WorkItemToUpdate = NULL;
    PLSAP_DB_LOOKUP_WORK_ITEM NewWorkItem = NULL;
    PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
    LONG DomainIndex;
    PLSAP_DB_LOOKUP_WORK_ITEM IsolatedNamesWorkItem = NULL;
    BOOLEAN AcquiredTrustedDomainListReadLock = FALSE;
    PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION TrustedDomainListSection = NULL;
    ULONG TrustedDomainListSectionIndex;
    PLSAPR_UNICODE_STRING DomainName = NULL;
    PLSAPR_UNICODE_STRING TerminalName = NULL;

    //
    // Create an empty Work List.
    //

    Status = LsapDbLookupCreateWorkList(&OutputWorkList);

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesBuildWorkListError;
    }

    //
    // Initialize the Work List Header.
    //

    OutputWorkList->Status = STATUS_SUCCESS;
    OutputWorkList->State = InactiveWorkList;
    OutputWorkList->LookupType = LookupNames;
    OutputWorkList->Count = Count;
    OutputWorkList->LookupLevel = LookupLevel;
    OutputWorkList->ReferencedDomains = ReferencedDomains;
    OutputWorkList->MappedCount = MappedCount;
    OutputWorkList->CompletelyUnmappedCount = CompletelyUnmappedCount;
    OutputWorkList->LookupNamesParams.Names = Names;
    OutputWorkList->LookupNamesParams.TranslatedSids = TranslatedSids;

    //
    // Construct the array of Work Items.  Each Work Item will
    // contain all the Names for a given domain, so we will scan
    // all of the Names, sorting them into Work Items as we go.
    // For each Name, follow the steps detailed below.
    //

    for (NameIndex = 0; NameIndex < Count; NameIndex++) {

        //
        // If this Name's Domain is already marked as known, skip.
        //

        if (TranslatedSids->Sids[NameIndex].DomainIndex != LSA_UNKNOWN_INDEX) {

            continue;
        }

        //
        // Name is completely unknown.  See if there is already a Work Item
        // for its Domain.
        //

        AnchorWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) OutputWorkList->AnchorWorkItem;
        NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) AnchorWorkItem->Links.Flink;;
        WorkItemToUpdate = NULL;
        NewWorkItem = NULL;

        //
        // If this is a qualified name (e.g. "NtDev\ScottBi") proceed
        // to search for its Domain.
        //

        DomainName = &PrefixNames[ NameIndex ];
        TerminalName = &SuffixNames[ NameIndex ];

        if (DomainName->Length != 0) {

            while (NextWorkItem != AnchorWorkItem) {

                if (RtlEqualDomainName(
                        (PUNICODE_STRING) &NextWorkItem->TrustInformation.Name,
                        (PUNICODE_STRING) DomainName
                        ))  {

                    //
                    // A Work Item already exists for the Name's Trusted Domain.
                    // Select that Work Item for update.
                    //

                    WorkItemToUpdate = NextWorkItem;

                    break;
                }

                //
                // Name's domain not found among existing Work Items.  Skip to
                // next Work Item.
                //

                NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) NextWorkItem->Links.Flink;
            }

            if (WorkItemToUpdate == NULL) {

                //
                // No Work Item exists for the Name's Domain.  See if the
                // Name belongs to one of the Trusted Domains.  If not, skip
                // to the next Name.
                //

                if (!AcquiredTrustedDomainListReadLock) {

                    Status = LsapDbAcquireReadLockTrustedDomainList( NULL );

                    if (!NT_SUCCESS(Status)) {

                        break;
                    }

                    AcquiredTrustedDomainListReadLock = TRUE;
                }

                Status = LsapDbLookupNameTrustedDomainList(
                             NULL,
                             DomainName,
                             &TrustInformation
                             );

                if (!NT_SUCCESS(Status)) {

                    if (Status == STATUS_NO_SUCH_DOMAIN) {

                        Status = STATUS_SUCCESS;
                        continue;
                    }

                    break;
                }

                //
                // Name belongs to a Trusted Domain for which there is
                // no Work Item.  Add the Domain to the Referenced Domain List
                // and obtain a Domain Index.
                //

                Status = LsapDbLookupAddListReferencedDomains(
                             ReferencedDomains,
                             TrustInformation,
                             &DomainIndex
                             );

                if (!NT_SUCCESS(Status)) {

                    break;
                }

                //
                // Create a new Work Item for this domain.
                //

                Status = LsapDbLookupCreateWorkItem(
                             TrustInformation,
                             DomainIndex,
                             (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
                             &NewWorkItem
                             );

                if (!NT_SUCCESS(Status)) {

                    break;
                }

                //
                // Add the Work Item to the List.
                //

                Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );

                if (!NT_SUCCESS(Status)) {

                    break;
                }

                WorkItemToUpdate = NewWorkItem;
            }

            //
            // Add the Name Index to the Work Item.
            //

            Status = LsapDbLookupAddIndicesToWorkItem(
                         WorkItemToUpdate,
                         (ULONG) 1,
                         &NameIndex
                         );

            if (!NT_SUCCESS(Status)) {

                break;
            }

            //
            // Store the Domain Index in the Translated Sids array entry for
            // the Name.
            //

            OutputWorkList->LookupNamesParams.TranslatedSids->Sids[NameIndex].DomainIndex = WorkItemToUpdate->DomainIndex;

            if (!NT_SUCCESS(Status)) {

                goto LookupNamesBuildWorkListError;
            }

        } else {

            //
            // This is an Isolated Name.  We know that it is not the name
            // of any Domain on the lookup path, because all of these
            // have been translated earlier.  We will add the name to a
            // temporary work item and later transfer all the Isolated Names
            // to every Work Item on the Work List.  If we don't already
            // have a temporary Work Item for the Isolated Names, create one.
            //

            if (IsolatedNamesWorkItem == NULL) {

                //
                // Create a new Work Item for the Isolated Names.
                // This is temporary.
                //

                Status = LsapDbLookupCreateWorkItem(
                             NULL,
                             DomainIndex,
                             (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
                             &IsolatedNamesWorkItem
                             );

                if (!NT_SUCCESS(Status)) {

                    break;
                }
            }

            //
            // Mark this Work Item as having Isolated Names only.
            //

            IsolatedNamesWorkItem->Properties = LSAP_DB_LOOKUP_WORK_ITEM_ISOL;

            //
            // Add the Name index to the Isolated Names Work Item
            //

            Status = LsapDbLookupAddIndicesToWorkItem(
                         IsolatedNamesWorkItem,
                         (ULONG) 1,
                         &NameIndex
                         );

            if (!NT_SUCCESS(Status)) {

                break;
            }
        }
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesBuildWorkListError;
    }

    //
    // If we have any unmapped Isolated Names, we know at this stage that they are
    // not the Names of Trusted Domains.  Therefore, we need to arrange
    // for them to be looked up in every Trusted Domain.  We need to
    // traverse the list of Trusted Domains and for each Domain, either
    // create a new Work Item to lookup all of the Names in that Domain,
    // or add the Names to the existing Work Item generated for that
    // Domain (because there are some Qualified Names which reference
    // the Domain.  We do this Work Item Generation in 2 stages.  First,
    // we will scan the Work List, adding the Isolated Names to every Work
    // Item found therein.  Second, we will create a Work Item for each
    // of the Trusted Domains that we don't already have a Work Item.
    //

    //
    // Stage (1) - Scan the Work List, adding the Isolated Names to
    //             every Work Item found therein
    //

    if (IsolatedNamesWorkItem != NULL) {

        //
        // Stage (1) - Scan the Work List, adding the Isolated Names to
        //             every Work Item found therein
        //

        NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
            OutputWorkList->AnchorWorkItem->Links.Flink;

        while (NextWorkItem != OutputWorkList->AnchorWorkItem) {

            //
            // Add the Isolated Name indices to this Work Item
            //

            Status = LsapDbLookupAddIndicesToWorkItem(
                         NextWorkItem,
                         IsolatedNamesWorkItem->UsedCount,
                         IsolatedNamesWorkItem->Indices
                         );

            if (!NT_SUCCESS(Status)) {

                break;
            }

            NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
                NextWorkItem->Links.Flink;
        }

        if (!NT_SUCCESS(Status)) {

            goto LookupNamesBuildWorkListError;
        }

        //
        // Stage (2) - Now create Work Items to look up all the Isolated Names
        // in every Trusted Domain that does not presently have a Work Item.
        // The Domains for all existing Work Items are already present
        // on the Referenced Domains List because they all specify at least one
        // Qualified Name, so we can lookup that list to determine whether a
        // Work Item exists for a Domain.
        //

        TrustedDomainListSection = NULL;
        TrustedDomainListSectionIndex = (ULONG) 0;

        for (;;) {

            NewWorkItem = NULL;

            Status = LsapDbTraverseTrustedDomainList(
                         NULL,
                         &TrustedDomainListSection,
                         &TrustedDomainListSectionIndex,
                         &TrustInformation
                         );

            if (!NT_SUCCESS(Status)) {

                if (Status == STATUS_NO_MORE_ENTRIES) {

                    Status = STATUS_SUCCESS;
                }

                break;
            }

            //
            // Found Next Trusted Domain.  Check if this domain is already
            // present on the Referenced Domain List.  If so, skip to the
            // next domain, because we have a Work Item for this domain.
            //
            // If the Domain is not present on the Referenced Domain List,
            // we need to create a Work Item for this Domain and add all
            // of the unmapped Isolated Names indices to it.
            //

            if (LsapDbLookupListReferencedDomains(
                    ReferencedDomains,
                    TrustInformation->Sid,
                    &DomainIndex
                    )) {

                continue;
            }

            //
            // We don't have a Work Item for this Trusted Domain.  Create
            // one, and add all of the remaining Isolated Names to it.
            // Mark the Domain Index as unknown.  This is picked up
            // later when the Work Item is processed.  If any Names
            // were translated, the Domain to which the Work Item
            // relates will be added to the Referenced Domain List.
            //

            Status = LsapDbLookupCreateWorkItem(
                         TrustInformation,
                         LSA_UNKNOWN_INDEX,
                         (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
                         &NewWorkItem
                         );

            if (!NT_SUCCESS(Status)) {

                break;
            }

            //
            // Mark this Work Item as having Isolated Names only.
            //

            NewWorkItem->Properties = LSAP_DB_LOOKUP_WORK_ITEM_ISOL;

            //
            // Add the Isolated Name indices to this Work Item
            //

            Status = LsapDbLookupAddIndicesToWorkItem(
                         NewWorkItem,
                         IsolatedNamesWorkItem->UsedCount,
                         IsolatedNamesWorkItem->Indices
                         );

            if (!NT_SUCCESS(Status)) {

                break;
            }

            //
            // Add the Work Item to the List.
            //

            Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );

            if (!NT_SUCCESS(Status)) {

                break;
            }

        }

        if (!NT_SUCCESS(Status)) {

            goto LookupNamesBuildWorkListError;
        }
    }

    //
    // If the Work List has no Work Items, this means that all of the
    // Names were composite, but none of the Domain Names specified
    // could be found on the Trusted Domain List.  In this case,
    // we discard the Work List.
    //

    Status = STATUS_NONE_MAPPED;

    if (OutputWorkList->WorkItemCount == 0) {

        goto LookupNamesBuildWorkListError;
    }

    //
    // Compute the Advisory Thread Count for this lookup.
    //

    Status = LsapDbLookupComputeAdvisoryChildThreadCount( OutputWorkList );

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesBuildWorkListError;
    }

    Status = LsapDbLookupInsertWorkList(OutputWorkList);

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesBuildWorkListError;
    }

    //
    // Update the Mapped Counts
    //

    LsapDbUpdateMappedCountsWorkList( OutputWorkList );

LookupNamesBuildWorkListFinish:

    //
    // If necessary, release the Trusted Domain List Read Lock.
    //

    if (AcquiredTrustedDomainListReadLock) {

        LsapDbReleaseReadLockTrustedDomainList( NULL );
        AcquiredTrustedDomainListReadLock = FALSE;
    }

    //
    // If we have an Isolated Names Work Item, free it.
    //

    if (IsolatedNamesWorkItem != NULL) {

        MIDL_user_free( IsolatedNamesWorkItem->Indices);
        IsolatedNamesWorkItem->Indices = NULL;
        MIDL_user_free( IsolatedNamesWorkItem );
        IsolatedNamesWorkItem = NULL;
    }

    *WorkList = OutputWorkList;
    return(Status);

LookupNamesBuildWorkListError:

    //
    // Discard the Work List.
    //

    if (OutputWorkList != NULL) {

        IgnoreStatus = LsapDbLookupDeleteWorkList(OutputWorkList);
        OutputWorkList = NULL;
    }

    goto LookupNamesBuildWorkListFinish;
}


NTSTATUS
LsapDbLookupSidsBuildWorkList(
    IN ULONG Count,
    IN PLSAPR_SID *Sids,
    IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN PLSAPR_TRANSLATED_NAMES TranslatedNames,
    IN LSAP_LOOKUP_LEVEL LookupLevel,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount,
    OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
    )

/*++

Routine Description:

    This function constructs a Work List for an LsarLookupSids call.  The
    Work List contains the parameters of the call, and an array of Work Items.
    Each Work Item specifies all of the Sids belonging to a given Domain
    and is the minimal unit of work that a worker thread will undertake.

    Count - Number of Sids in the Sids array,  Note that some of these
        may already have been mapped elsewhere, as specified by the
        MappedCount parameter.

    Sids - Pointer to array of pointers to Sids to be translated.
        Zero or all of the Sids may already have been translated
        elsewhere.  If any of the Sids have been translated, the

        Names parameter will point to a location containing a non-NULL
        array of Name translation structures corresponding to the
        Sids.  If the nth Sid has been translated, the nth name
        translation structure will contain either a non-NULL name
        or a non-negative offset into the Referenced Domain List.  If
        the nth Sid has not yet been translated, the nth name
        translation structure will contain a zero-length name string
        and a negative value for the Referenced Domain List index.

    TrustInformation - Pointer to Trust Information specifying a Domain Sid
        and Name.

    ReferencedDomains - Pointer to a Referenced Domain List structure.
        The structure references an array of zero or more Trust Information
        entries, one per referenced domain.  This array will be appended to
        or reallocated if necessary.

    TranslatedNames - Pointer to structure that optionally references a list
        of name translations for some of the Sids in the Sids array.

    LookupLevel - Specifies the Level of Lookup to be performed on this
        machine.  Values of this field are are follows:

        LsapLookupPDC - Second Level Lookup performed on a Primary Domain
            Controller.  The lookup searches the Account Domain of the
            SAM Database on the controller.  If not all Sids or Names are
            found, the Trusted Domain List (TDL) is obtained from the
            LSA's Policy Database and Third Level lookups are performed
            via "handoff" to each Trusted Domain in the List.

        LsapLookupTDL - Third Level Lookup performed on a controller
            for a Trusted Domain.  The lookup searches the Account Domain of
            the SAM Database on the controller only.

        NOTE:  LsapLookupWksta is not valid for this parameter.

    MappedCount - Pointer to location containing the number of Sids
        in the Sids array that have already been mapped.  This number
        will be updated to reflect additional mapping done by this
        routine.

    CompletelyUnmappedCount - Pointer to location containing the
        count of completely unmapped Sids.  A Sid is completely unmapped
        if it is unknown and also its Domain Prefix Sid is not recognized.
        This count is updated on exit, the number of completely unmapped
        Sids whose Domain Prefices are identified by this routine being
        subtracted from the input value.

    WorkList - Receives pointer to completed Work List if successfully built.

Return Values:

    NTSTATUS - Standard Nt Result Code.

        STATUS_NONE_MAPPED - None of the Sids specified belong to any of
            the Trusted Domains.  No Work List has been generated.  Note
            that this is not a fatal error.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    NTSTATUS IgnoreStatus = STATUS_SUCCESS;
    PLSAP_DB_LOOKUP_WORK_LIST OutputWorkList = NULL;
    ULONG SidIndex;
    PSID DomainSid = NULL;
    PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
    PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
    PLSAP_DB_LOOKUP_WORK_ITEM WorkItemToUpdate = NULL;
    PLSAP_DB_LOOKUP_WORK_ITEM NewWorkItem = NULL;
    PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
    LONG DomainIndex;
    PSID Sid = NULL;
    BOOLEAN AcquiredReadLockTrustedDomainList = FALSE;

    //
    // Create an empty Work List
    //

    Status = LsapDbLookupCreateWorkList(&OutputWorkList);

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsBuildWorkListError;
    }

    //
    // Initialize the rest of the Work List Header fields.  Some fields
    // were initialized upon creation to fixed values.  The ones set here
    // depend on parameter values passed into this routine.
    //

    OutputWorkList->LookupType = LookupSids;
    OutputWorkList->Count = Count;
    OutputWorkList->LookupLevel = LookupLevel;
    OutputWorkList->ReferencedDomains = ReferencedDomains;
    OutputWorkList->MappedCount = MappedCount;
    OutputWorkList->CompletelyUnmappedCount = CompletelyUnmappedCount;
    OutputWorkList->LookupSidsParams.Sids = Sids;
    OutputWorkList->LookupSidsParams.TranslatedNames = TranslatedNames;

    //
    // Construct the array of Work Items.  Each Work Item will
    // contain all the Sids for a given domain, so we will scan
    // all of the Sids, sorting them into Work Items as we go.
    // For each Sid, follow the steps detailed below.
    //

    for (SidIndex = 0; SidIndex < Count; SidIndex++) {

        //
        // If this Sid's Domain is already marked as known, skip.
        //

        if (TranslatedNames->Names[SidIndex].DomainIndex != LSA_UNKNOWN_INDEX) {

            continue;
        }

        //
        // Sid is completely unknown.  Extract its Domain Sid and see if
        // there is already a Work Item for its Domain.
        //

        Sid = Sids[SidIndex];

        Status = LsapRtlExtractDomainSid( Sid, &DomainSid );

        if (!NT_SUCCESS(Status)) {

            break;
        }

        NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) OutputWorkList->AnchorWorkItem->Links.Flink;
        AnchorWorkItem = OutputWorkList->AnchorWorkItem;
        WorkItemToUpdate = NULL;
        NewWorkItem = NULL;

        while (NextWorkItem != AnchorWorkItem) {

            if (RtlEqualSid((PSID) NextWorkItem->TrustInformation.Sid,DomainSid)) {

                //
                // A Work Item already exists for the Sid's Trusted Domain.
                // Select that Work Item for update.
                //

                MIDL_user_free(DomainSid);
                WorkItemToUpdate = NextWorkItem;
                break;
            }

            //
            // Sid's domain not found among existing Work Items.  Skip to
            // next Work Item.
            //

            NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) NextWorkItem->Links.Flink;
        }

        if (WorkItemToUpdate == NULL) {

            //
            // No Work Item exists for the Sid's Domain.  See if the
            // Sid belongs to one of the Trusted Domains.  If not, skip
            // to the next Sid.
            //

            if (!AcquiredReadLockTrustedDomainList) {

                Status = LsapDbAcquireReadLockTrustedDomainList( NULL );

                if (!NT_SUCCESS(Status)) {

                    break;
                }

                AcquiredReadLockTrustedDomainList = TRUE;
            }

            Status = LsapDbLookupSidTrustedDomainList(
                         NULL,
                         DomainSid,
                         &TrustInformation
                         );

            MIDL_user_free(DomainSid);

            if ( !NT_SUCCESS(Status) ) {

                if (Status == STATUS_NO_SUCH_DOMAIN) {

                    //
                    // Try looking up the sid itself as a domain
                    //

                    Status = LsapDbLookupSidTrustedDomainList(
                                NULL,
                                Sid,
                                &TrustInformation
                                );


                    if (Status == STATUS_NO_SUCH_DOMAIN) {

                        Status = STATUS_SUCCESS;
                        continue;
                    }
                }

                if ( !NT_SUCCESS(Status) ) {
                    break;
                }
            }

            //
            // Sid belongs to a Trusted Domain for which there is
            // no Work Item.  Add the Domain to the Referenced Domain List
            // and obtain a Domain Index.
            //

            Status = LsapDbLookupAddListReferencedDomains(
                         ReferencedDomains,
                         TrustInformation,
                         &DomainIndex
                         );

            if (!NT_SUCCESS(Status)) {

                break;
            }

            //
            // Create a new Work Item for this domain.
            //

            Status = LsapDbLookupCreateWorkItem(
                         TrustInformation,
                         DomainIndex,
                         (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
                         &NewWorkItem
                         );

            if (!NT_SUCCESS(Status)) {

                break;
            }

            //
            // Add the Work Item to the List.
            //

            Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );

            if (!NT_SUCCESS(Status)) {

                break;
            }

            WorkItemToUpdate = NewWorkItem;
        }

        //
        // Add the Sid Index to the Work Item.
        //

        Status = LsapDbLookupAddIndicesToWorkItem(
                     WorkItemToUpdate,
                     (ULONG) 1,
                     &SidIndex
                     );

        if (!NT_SUCCESS(Status)) {

            break;
        }

        //
        // Store the Domain Index in the Translated Names array entry for
        // the Sid.
        //

        OutputWorkList->LookupSidsParams.TranslatedNames->Names[SidIndex].DomainIndex = WorkItemToUpdate->DomainIndex;
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsBuildWorkListError;
    }

    //
    // If the Work List has no Work Items, this means that none of the
    // Sids belong to any of the Trusted Domains.  In this case,
    // we discard the Work List.
    //

    Status = STATUS_NONE_MAPPED;

    if (OutputWorkList->WorkItemCount == 0) {

        goto LookupSidsBuildWorkListError;
    }

    //
    // Compute the Advisory Thread Count for this lookup.
    //

    Status = LsapDbLookupComputeAdvisoryChildThreadCount( OutputWorkList );

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsBuildWorkListError;
    }

    //
    // Insert the Work List at the end of the Work Queue.
    //

    Status = LsapDbLookupInsertWorkList(OutputWorkList);

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsBuildWorkListError;
    }

    //
    // Update the Mapped Counts
    //

    LsapDbUpdateMappedCountsWorkList( OutputWorkList );

    *WorkList = OutputWorkList;

LookupSidsBuildWorkListFinish:

    //
    // If necessary, release the Trusted Domain List Read Lock.
    //

    if (AcquiredReadLockTrustedDomainList) {

        LsapDbReleaseReadLockTrustedDomainList( NULL );
        AcquiredReadLockTrustedDomainList = FALSE;
    }

    return(Status);

LookupSidsBuildWorkListError:

    if ( OutputWorkList != NULL ) {

        IgnoreStatus = LsapDbLookupDeleteWorkList( OutputWorkList );
        OutputWorkList = NULL;
    }

    *WorkList = NULL;
    goto LookupSidsBuildWorkListFinish;
}


NTSTATUS
LsapDbLookupCreateWorkList(
    OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
    )

/*++

Routine Description:

    This function creates a Lookup Operation Work List and
    initializes fixed default fields.

Arguments:

    WorkList - Receives Pointer to an empty Work List structure.

Return Value:

    NTSTATUS - Standard Nt Result Code

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    //
    // Allocate memory for the Work List header.
    //

    *WorkList = LsapAllocateLsaHeap( sizeof(LSAP_DB_LOOKUP_WORK_LIST) );

    //
    // Initialize the fixed fields in the Work List.
    //

    Status = LsapDbLookupInitializeWorkList(*WorkList);

    return(Status);
}


NTSTATUS
LsapDbLookupInsertWorkList(
    IN PLSAP_DB_LOOKUP_WORK_LIST WorkList
    )

/*++

Routine Description:

    This function inserts a Lookup Operation Work List in the Work Queue.

Arguments:

    WorkList - Pointer to a Work List structure describing a Lookup Sids
        or Lookup Names operation.

Return Value:

    NTSTATUS - Standard Nt Result Code
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    BOOLEAN AcquiredWorkQueueLock = FALSE;

    //
    // Acquire the Lookup Work Queue Lock.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto LookupInsertWorkListError;
    }

    AcquiredWorkQueueLock = TRUE;

    //
    // Mark the Work List as Active.
    //

    WorkList->State = ActiveWorkList;

    //
    // Link the Work List onto the end of the Work Queue.
    //

    WorkList->WorkLists.Flink =
        (PLIST_ENTRY) LookupWorkQueue.AnchorWorkList;
    WorkList->WorkLists.Blink =
        (PLIST_ENTRY) LookupWorkQueue.AnchorWorkList->WorkLists.Blink;

    WorkList->WorkLists.Flink->Blink = (PLIST_ENTRY) WorkList;
    WorkList->WorkLists.Blink->Flink = (PLIST_ENTRY) WorkList;

    //
    // Update the Currently Assignable Work List and Work Item pointers
    // if there is none.
    //

    if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {

        LookupWorkQueue.CurrentAssignableWorkList = WorkList;
        LookupWorkQueue.CurrentAssignableWorkItem =
            (PLSAP_DB_LOOKUP_WORK_ITEM) WorkList->AnchorWorkItem->Links.Flink;
    }


    //
    // Diagnostic message indicating work list has been inserted
    //

    LsapDiagPrint( DB_LOOKUP_WORK_LIST,
                   ("LSA DB: Inserting WorkList: 0x%lx ( Item Count: %ld)\n", WorkList, WorkList->WorkItemCount) );


LookupInsertWorkListFinish:

    //
    // If necessary, release the Lookup Work Queue Lock.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupInsertWorkListError:

    goto LookupInsertWorkListFinish;
}


NTSTATUS
LsapDbLookupDeleteWorkList(
    IN PLSAP_DB_LOOKUP_WORK_LIST WorkList
    )

/*++

Routine Description:

    This function Deletes a Lookup Operation Work List from the Work Queue
    and frees the Work List structure.

Arguments:

    WorkList - Pointer to a Work List structure describing a Lookup Sids
        or Lookup Names operation.

Return Value:

    NTSTATUS - Standard Nt Result Code
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_DB_LOOKUP_WORK_ITEM ThisWorkItem = NULL;
    PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
    BOOLEAN AcquiredWorkQueueLock = FALSE;

    //
    // Acquire the Lookup Work Queue Lock.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto LookupDeleteWorkListError;
    }

    AcquiredWorkQueueLock = TRUE;

    //
    // An internal error exists if we are trying to delete an active Work List.
    // Only inactive or completed Work Lists can be removed.
    //

    ASSERT(WorkList->State != ActiveWorkList);

    //
    // If the Work List is on the Work Queue, remove it.
    //

    if ((WorkList->WorkLists.Blink != NULL) &&
        (WorkList->WorkLists.Flink != NULL)) {

        WorkList->WorkLists.Blink->Flink = WorkList->WorkLists.Flink;
        WorkList->WorkLists.Flink->Blink = WorkList->WorkLists.Blink;
    }

    //
    // Release the Lookup Work Queue Lock.
    //

    LsapDbLookupReleaseWorkQueueLock();
    AcquiredWorkQueueLock = FALSE;

    //
    // Free up memory allocated for the Work Items on the List.
    //

    ThisWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) WorkList->AnchorWorkItem->Links.Blink;

    while (ThisWorkItem != WorkList->AnchorWorkItem) {

        NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) ThisWorkItem->Links.Blink;

        if (ThisWorkItem->Indices != NULL) {

            MIDL_user_free( ThisWorkItem->Indices );
        }

        MIDL_user_free( ThisWorkItem->TrustInformation.Sid );

        MIDL_user_free( ThisWorkItem->TrustInformation.Name.Buffer );

        MIDL_user_free( ThisWorkItem );

        ThisWorkItem = NextWorkItem;
    }

    //
    // Free up memory allocated for the Work List structure itself.
    //

    MIDL_user_free( WorkList );

LookupDeleteWorkListFinish:

    //
    // If necessary, release the Lookup Work Queue Lock.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupDeleteWorkListError:

    goto LookupDeleteWorkListFinish;
}


VOID
LsapDbUpdateMappedCountsWorkList(
    IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
    )

/*++

Routine Decsription:

    This function updates the counts of completely Mapped and completely
    Unmapped Sids or Names in a Work List.  A Sid or Name is completely
    mapped if its Use has been identified, partially mapped if its
    Domain is known but its terminal name of relative id is not yet
    known, completely unmapped if its domain is not yet known.

Arguments:

    WorkList - Pointer to Work List to be updated.

Return Values:

    None.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    BOOLEAN AcquiredWorkQueueLock = FALSE;
    ULONG OutputMappedCount = (ULONG) 0;
    ULONG OutputCompletelyUnmappedCount = WorkList->Count;
    ULONG Index;

    //
    // Acquire the Lookup Work Queue Lock.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto UpdateMappedCountsWorkListError;
    }

    AcquiredWorkQueueLock = TRUE;

    if (WorkList->LookupType == LookupSids) {

        for ( Index = (ULONG) 0; Index < WorkList->Count; Index++ ) {

            if (WorkList->LookupSidsParams.TranslatedNames->Names[ Index].Use
                   != SidTypeUnknown) {

                OutputMappedCount++;
                OutputCompletelyUnmappedCount--;

            } else if (WorkList->LookupSidsParams.TranslatedNames->Names[ Index].DomainIndex
                           != LSA_UNKNOWN_INDEX) {

                OutputCompletelyUnmappedCount--;
            }
        }

    } else {

        for ( Index = (ULONG) 0; Index < WorkList->Count; Index++ ) {

            if (WorkList->LookupNamesParams.TranslatedSids->Sids[ Index].Use
                   != SidTypeUnknown) {

                OutputMappedCount++;
                OutputCompletelyUnmappedCount--;

            } else if (WorkList->LookupNamesParams.TranslatedSids->Sids[ Index].DomainIndex
                           != LSA_UNKNOWN_INDEX) {

                OutputCompletelyUnmappedCount--;
            }
        }
    }

    *WorkList->MappedCount = OutputMappedCount;
    *WorkList->CompletelyUnmappedCount = OutputCompletelyUnmappedCount;

UpdateMappedCountsWorkListFinish:

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return;

UpdateMappedCountsWorkListError:

    goto UpdateMappedCountsWorkListFinish;
}


NTSTATUS
LsapDbLookupSignalCompletionWorkList(
    IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
    )

/*++

Routine Description:

    This function signals the completion or termination of work on
    a Work List.

Arguments:

    WorkList - Pointer to a Work List structure describing a Lookup Sids
        or Lookup Names operation.

Return Value:

    NTSTATUS - Standard Nt Result Code
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    BOOLEAN AcquiredWorkQueueLock = FALSE;

    //
    // Verify that all work on the Work List is either complete or
    // the Work List has been terminated.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto LookupSignalCompletionWorkListError;
    }

    AcquiredWorkQueueLock = TRUE;

    if (NT_SUCCESS(WorkList->Status)) {

        Status = STATUS_INTERNAL_DB_ERROR;

        if (WorkList->CompletedWorkItemCount != WorkList->WorkItemCount) {

            goto LookupSignalCompletionWorkListError;
        }
    }

    //
    // Signal the event that indicates that a Work List has been processed.
    //

    Status = NtSetEvent( LsapDbLookupCompleteEvent, NULL );


    if (!NT_SUCCESS(Status)) {

        LsapDiagPrint( DB_LOOKUP_WORK_LIST,
                     ("LSA DB: LsapDbLookupSignalCompletion.. NtSetEvent failed 0x%lx\n",Status));
        goto LookupSignalCompletionWorkListError;
    }

LookupSignalCompletionWorkListFinish:


    LsapDiagPrint( DB_LOOKUP_WORK_LIST,
                   ("LSA DB: Lookup completion event signalled. (Status: 0x%lx)\n"
                    "            WorkList: 0x%lx\n", Status, WorkList) );

    //
    // If necessary, release the Lookup Work Queue Lock.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupSignalCompletionWorkListError:

    goto LookupSignalCompletionWorkListFinish;
}


NTSTATUS
LsapDbLookupAwaitCompletionWorkList(
    IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
    )

/*++

Routine Description:

    This function awaits the completion or termination of work on a
    specified Work List.

    NOTE:  This routine expects the specified pointer to a Work List to be
           valid.  A Work List pointer always remains valid until its
           Primary thread detects completion of the Work List via this
           routine and then deletes it via LsapDbLookupDeleteWorkList().
           For this reason, the Lookup Work Queue lock does not have to
           be held while this routine executes.

Arguments:

    WorkList - Pointer to a Work List structure describing a Lookup Sids
        or Lookup Names operation.

Return Value:

    NTSTATUS - Standard Nt Result Code

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    LSAP_DB_LOOKUP_WORK_LIST_STATE WorkListState;
    BOOLEAN AcquiredWorkQueueLock = FALSE;

    //
    // Loop, waiting for completion events to occur.  When one does,
    // check the status of the specified Work List.
    //

    for (;;) {

        //
        // Check for completed Work List.  Since someone else may be
        // setting the state, we need to read it while holding the lock.
        //

        Status = LsapDbLookupAcquireWorkQueueLock();

        if (!NT_SUCCESS(Status)) {

            break;
        }

        AcquiredWorkQueueLock = TRUE;

        WorkListState = WorkList->State;

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;

        if (WorkListState == CompletedWorkList) {

            break;
        }

        //
        // Wait for Work List completed event to be signalled.
        //

        LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("Lsa Db: Waiting on worklist completion event\n") );
        Status = NtWaitForSingleObject( LsapDbLookupCompleteEvent, TRUE, NULL);
        LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LsapDb: Wait on worklist completion event Done\n        Status: 0x%lx\n", Status) );

        if (!NT_SUCCESS(Status)) {

            break;
        }
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupAwaitCompletionWorkListError;
    }

LookupAwaitCompletionWorkListFinish:

    //
    // If necessary, release the Lookup Work Queue Lock.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupAwaitCompletionWorkListError:


    LsapDiagPrint( DB_LOOKUP_WORK_LIST,
                   ("Lsa Db: LookupAwaitWorklist error. (Status: 0x%lx)\n", Status) );

    goto LookupAwaitCompletionWorkListFinish;
}


NTSTATUS
LsapDbAddWorkItemToWorkList(
    IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
    IN PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
    )

/*++

Routine Decsription:

    This function adds a Work Item to a Work List.  The specified
    Work Item must exist in non-volatile memory (e.g a heap block).

Arguments:

    WorkList - Pointer to a Work List structure describing a Lookup Sids
        or Lookup Names operation.

    WorkItem - Pointer to a Work Item structure describing a list of
        Sids or Names and a domain in which they are to be looked up.

Return Value:

    NTSTATUS - Standard Nt Result Code
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    BOOLEAN AcquiredWorkQueueLock = FALSE;

    //
    // Acquire the Lookup Work Queue Lock.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto LookupAddWorkItemToWorkListError;
    }

    AcquiredWorkQueueLock = TRUE;

    //
    // Mark the Work Item as assignable.
    //

    WorkItem->State = AssignableWorkItem;

    //
    // Link the Work Item onto the end of the Work List and increment the
    // Work Item Count.
    //

    WorkItem->Links.Flink = (PLIST_ENTRY) WorkList->AnchorWorkItem;
    WorkItem->Links.Blink = (PLIST_ENTRY) WorkList->AnchorWorkItem->Links.Blink;
    WorkItem->Links.Flink->Blink = (PLIST_ENTRY) WorkItem;
    WorkItem->Links.Blink->Flink = (PLIST_ENTRY) WorkItem;

    WorkList->WorkItemCount++;

LookupAddWorkItemToWorkListFinish:

    //
    // If necessary, release the Lookup Work Queue Lock.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupAddWorkItemToWorkListError:

    goto LookupAddWorkItemToWorkListFinish;
}


VOID
LsapDbLookupWorkerThreadStart(
    )

/*++

Routine Description:

    This routine initiates a child Worker Thread for a Lookup operation.

Arguments:

    None.

Return Value:

    None.

--*/

{
    //
    // Start the thread's work processing loop, specifying that this
    // thread is a child thread rather than the main thread.
    //

    LsapDbLookupWorkerThread( FALSE );
}


VOID
LsapDbLookupWorkerThread(
    IN BOOLEAN PrimaryThread
    )

/*++

Routine Description:

    This function is executed by each worker thread for a Lookup operation.
    Each worker thread loops, requesting work items from the Lookup
    Work Queue.  Work Items assigned may belong to any current lookup.

Arguments:

    PrimaryThread - TRUE if thread is the main thread of the Lookup
        operation, FALSE if the thread is a child thread created by
        the Lookup operation.  The main thread of the Lookup operation
        also processes work items, but is also responsible for collating
        the results of the Lookup operation.  It is not counted in the
        active thread count and is not returnable to the thread pool.

Return Value:

    None.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;
    PLSAP_DB_LOOKUP_WORK_ITEM WorkItem = NULL;
    BOOLEAN AcquiredWorkQueueLock = FALSE;

    //
    // If this thread is a child worker thread, increment count of active
    // child threads.
    //

    if (!PrimaryThread) {

        Status = LsapDbLookupAcquireWorkQueueLock();

        if (!NT_SUCCESS(Status)) {

            goto LookupWorkerThreadError;
        }

        AcquiredWorkQueueLock = TRUE;

        LookupWorkQueue.ActiveChildThreadCount++;

        LsapDbLookupReleaseWorkQueueLock();

        AcquiredWorkQueueLock = FALSE;
    }

    //
    // Loop while there is work to do.
    //

    for (;;) {

        //
        // Obtain work packet
        //

        Status = LsapDbLookupObtainWorkItem(&WorkList, &WorkItem);

        if (NT_SUCCESS(Status)) {

            Status = LsapDbLookupProcessWorkItem(WorkList, WorkItem);

            if (NT_SUCCESS(Status)) {

                continue;
            }

            //
            // An error has occurred.  Stop this lookup.
            //

            Status = LsapDbLookupStopProcessingWorkList(WorkList, Status);

            //
            // NOTE:  Intentionally ignore the status.
            //

            Status = STATUS_SUCCESS;
        }

        //
        // If an error occurred other than there being no more work to do,
        // quit.
        //

        if (Status != STATUS_NO_MORE_ENTRIES) {

            break;
        }

        Status = STATUS_SUCCESS;

        //
        // There is no more work to do.  If this thread is a child worker
        // thread, either return thread to pool and wait for more work, or
        // terminate if enough threads have already been retained.  If this
        // thread is the main thread of a Lookup operation, just return
        // in order to collate results.
        //

        if (!PrimaryThread) {

            Status = LsapDbLookupAcquireWorkQueueLock();

            if (!NT_SUCCESS(Status)) {

                break;
            }

            AcquiredWorkQueueLock = TRUE;

            if (LookupWorkQueue.ActiveChildThreadCount <= LookupWorkQueue.MaximumRetainedChildThreadCount) {

                LsapDbLookupReleaseWorkQueueLock();
                AcquiredWorkQueueLock = FALSE;

                //
                // Wait forever for more work.
                //

                Status = NtWaitForSingleObject( LsapDbLookupStartedEvent, TRUE, NULL);

                if (NT_SUCCESS(Status)) {

                    continue;
                }

                //
                // An error occurred in the wait routine. Exit the thread.
                //

                Status = LsapDbLookupAcquireWorkQueueLock();

                if (!NT_SUCCESS(Status)) {

                    break;
                }

                AcquiredWorkQueueLock = TRUE;
            }

            //
            // We already have enough active threads or an error has occurred.
            // Mark this one inactive and terminate it.
            //

            LookupWorkQueue.ActiveChildThreadCount--;

            LsapDbLookupReleaseWorkQueueLock();
            AcquiredWorkQueueLock = FALSE;

            //
            // Terminate the thread.
            //

            ExitThread((DWORD) Status);
        }

        //
        // We're the Primary Thread of some Lookup operation and there is
        // no more work to do.  Break out so we can return to caller.
        //

        break;
    }

LookupWorkerThreadFinish:

    //
    // If necessary, release the Lookup Work Queue Lock.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return;

LookupWorkerThreadError:

    goto LookupWorkerThreadFinish;
}


NTSTATUS
LsapDbLookupObtainWorkItem(
    OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList,
    OUT PLSAP_DB_LOOKUP_WORK_ITEM *WorkItem
    )

/*++

Routine Description:

    This function is called by a worker thread to obtain a Work Item.  This
    Work Item may belong to any current lookup operation.

Arguments:

    WorkList - Receives a pointer to a Work List structure describing a
        Lookup Sids or Lookup Names operation.

    WorkItem - Receives a pointer to a Work Item structure describing a
        list of Sids or Names and a domain in which they are to be looked up.

Return Value:

    NTSTATUS - Standard Nt Result Code

        STATUS_NO_MORE_ENTRIES - No more work items available.
--*/

{
    NTSTATUS Status;
    BOOLEAN AcquiredWorkQueueLock = FALSE;
    *WorkList = NULL;
    *WorkItem = NULL;

    //
    // Acquire the Lookup Work Queue Lock.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto LookupObtainWorkItemError;
    }

    AcquiredWorkQueueLock = TRUE;

    //
    // Return an error if there are no more Work Items.
    //

    Status = STATUS_NO_MORE_ENTRIES;

    if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {

        goto LookupObtainWorkItemError;
    }

    //
    // Verify that the Current Assignable Work List does not have
    // a termination error.  This should never happen, because the
    // pointers should be updated if the Lookup corresponding to the Current
    // Assignable Work List is terminated.
    //

    ASSERT(NT_SUCCESS(LookupWorkQueue.CurrentAssignableWorkList->Status));

    //
    // There are work items available.  Check the next one out.
    //

    ASSERT(LookupWorkQueue.CurrentAssignableWorkItem->State == AssignableWorkItem);
    LookupWorkQueue.CurrentAssignableWorkItem->State = AssignedWorkItem;
    *WorkList = LookupWorkQueue.CurrentAssignableWorkList;
    *WorkItem = LookupWorkQueue.CurrentAssignableWorkItem;

    //
    // Update pointers to next item (if any) in the current Work List
    // where work is being given out.
    //

    Status = LsapDbLookupUpdateAssignableWorkItem(FALSE);

    if (!NT_SUCCESS(Status)) {

        goto LookupObtainWorkItemError;
    }

LookupObtainWorkItemFinish:

    //
    // If we acquired the Lookup Work Queue Lock, release it.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupObtainWorkItemError:

    goto LookupObtainWorkItemFinish;
}


NTSTATUS
LsapDbLookupProcessWorkItem(
    IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
    IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
    )

/*++

Routine Description:

    This function processes a Work Item for a Lookup operation.  The Work
    Item specifies a number of Sids or Names to be looked up in a given
    domain.

Arguments:

    WorkList - Pointer to a Work List structure describing a
        Lookup Sids or Lookup Names operation.

    WorkItem - Pointer to a Work Item structure describing a
        list of Sids or Names and a domain in which they are to be looked up.

Return Value:

    NTSTATUS - Standard Nt Result Code
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    NTSTATUS SecondaryStatus;
    ULONG NextLevelCount;
    ULONG NextLevelMappedCount;
    LSA_HANDLE ControllerPolicyHandle = NULL;
    ULONG NextLevelIndex;
    PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
    PSID *NextLevelSids = NULL;
    PUNICODE_STRING NextLevelNames = NULL;
    PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
    PLSA_TRANSLATED_SID NextLevelTranslatedSids = NULL;
    PLSA_TRANSLATED_NAME NextLevelTranslatedNames = NULL;
    ULONG Index;

    LsapDiagPrint( DB_LOOKUP_WORK_LIST,
           ("LSA DB: Processing work item. (0x%lx, 0x%lx)\n", WorkList, WorkItem));

    //
    // Open a Domain Controller for this Trusted Domain.
    //

    Status = LsapDbOpenPolicyTrustedDomain(
                 &WorkItem->TrustInformation,
                 POLICY_LOOKUP_NAMES,
                 &ControllerPolicyHandle
                 );

    //
    // If we are unable to connect to the Trusted Domain via any DC,
    // suppress the error so that the Lookup can continue to try and
    // translate other Sids/Names.
    //

    if (!NT_SUCCESS(Status)) {

        Status = STATUS_SUCCESS;
        goto LookupProcessWorkItemError;
    }

    //
    // Branch according to lookup type.
    //

    NextLevelCount = WorkItem->UsedCount;

    if (WorkList->LookupType == LookupSids) {

        //
        // Allocate an array for the Sids to be looked up at a Domain
        // Controller for the specified Trusted Domain.
        //

        NextLevelSids = MIDL_user_allocate( sizeof(PSID) * NextLevelCount );

        Status = STATUS_INSUFFICIENT_RESOURCES;

        if (NextLevelSids == NULL) {

            goto LookupProcessWorkItemError;
        }

        //
        // Copy in the Sids to be looked up from the Work List.  The Work
        // Item contains their indices relative to the Sid array in the
        // Work List.
        //

        for (NextLevelIndex = 0;
             NextLevelIndex < NextLevelCount;
             NextLevelIndex++) {

            Index = WorkItem->Indices[ NextLevelIndex ];
            NextLevelSids[NextLevelIndex] = WorkList->LookupSidsParams.Sids[Index];
        }

        Status = STATUS_SUCCESS;

        NextLevelMappedCount = (ULONG) 0;

        //
        // Lookup the Sids at the DC.
        //

        Status = LsaICLookupSids(
                     ControllerPolicyHandle,
                     NextLevelCount,
                     NextLevelSids,
                     (PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
                     (PLSA_TRANSLATED_NAME *) &NextLevelTranslatedNames,
                     WorkList->LookupLevel,
                     &NextLevelMappedCount
                     );

        LsapDiagPrint( DB_LOOKUP_WORK_LIST,
                   ("LSA DB: Sid Lookup.\n"
                    "            Item: (0x%lx, 0x%lx)\n"
                    "           Count: 0x%lx\n", WorkList, WorkItem, NextLevelCount));

        //
        // If the callout to LsaLookupSids() was unsuccessful, disregard
        // the error and set the domain name for any Sids having this
        // domain Sid as prefix sid.
        //

        if (!NT_SUCCESS(Status)) {

            Status = STATUS_SUCCESS;
            goto LookupProcessWorkItemFinish;
        }

        //
        // The callout to LsaICLookupSids() was successful.  Update the
        // TranslatedNames information in the Work List as appropriate
        // using the TranslatedNames information returned from the callout.
        //

        Status = LsapDbLookupSidsUpdateTranslatedNames(
                     WorkList,
                     WorkItem,
                     NextLevelTranslatedNames
                     );

        if (!NT_SUCCESS(Status)) {

            goto LookupProcessWorkItemError;
        }

    } else if (WorkList->LookupType == LookupNames) {

        //
        // Allocate an array of UNICODE_STRING structures for the
        // names to be looked up at the Domain Controller.
        //

        NextLevelNames = MIDL_user_allocate(sizeof(UNICODE_STRING) * NextLevelCount);

        if (NextLevelNames == NULL) {

            goto LookupProcessWorkItemError;
        }

        Status = STATUS_SUCCESS;

        //
        // Copy in the Names to be looked up from the Work List.  The Work
        // Item contains their indices relative to the Names array in the
        // Work List.
        //

        for (NextLevelIndex = 0;
             NextLevelIndex < NextLevelCount;
             NextLevelIndex++) {

            Index = WorkItem->Indices[ NextLevelIndex ];
            NextLevelNames[NextLevelIndex] =
                *((PUNICODE_STRING) &WorkList->LookupNamesParams.Names[Index]);
        }

        NextLevelMappedCount = (ULONG) 0;

        //
        // Lookup the Names at the DC.
        //

        Status = LsaICLookupNames(
                     ControllerPolicyHandle,
                     NextLevelCount,
                     NextLevelNames,
                     (PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
                     (PLSA_TRANSLATED_SID *) &NextLevelTranslatedSids,
                     WorkList->LookupLevel,
                     &NextLevelMappedCount
                     );

        //
        // If the callout to LsaLookupNames() was unsuccessful, disregard
        // the error and set the domain name for any Sids having this
        // domain Sid as prefix sid.
        //

        if (!NT_SUCCESS(Status)) {

            Status = STATUS_SUCCESS;
            goto LookupProcessWorkItemError;
        }

        //
        // The callout to LsaICLookupNames() was successful.  Update the
        // TranslatedSids information in the Work List as appropriate
        // using the TranslatedSids information returned from the callout.
        //

        Status = LsapDbLookupNamesUpdateTranslatedSids(
                     WorkList,
                     WorkItem,
                     NextLevelTranslatedSids
                     );

        if (!NT_SUCCESS(Status)) {

            goto LookupProcessWorkItemError;
        }

    } else {

        Status = STATUS_INVALID_PARAMETER;
        goto LookupProcessWorkItemError;
    }

LookupProcessWorkItemFinish:

    //
    // Change the state of the work item to "Completed"
    //

    WorkItem->State = CompletedWorkItem;


    //
    // Update the Mapped Counts
    //

    LsapDbUpdateMappedCountsWorkList( WorkList );


    //
    // Protect WorkList operations
    //

    Status = LsapDbLookupAcquireWorkQueueLock();
    if (!NT_SUCCESS(Status)) {
        goto LookupProcessWorkItemError;
    }

    //
    // Increment the count of completed Work Items whether or not this
    // one was completed without error.  If the Work List has just been
    // completed, change its state to "CompletedWorkList" and signal
    // the Lookup operation completed event.  Allow re-entry into
    // this section if an error is returned.
    //

    WorkList->CompletedWorkItemCount++;


    LsapDiagPrint( DB_LOOKUP_WORK_LIST,
                   ("LSA DB: Process Work Item Completed.\n"
                    "                       Item: (0x%lx, 0x%lx)\n"
                    "            Completed Count: %ld\n", WorkList, WorkItem, WorkList->CompletedWorkItemCount));

    if (WorkList->State != CompletedWorkList) {

        if (WorkList->CompletedWorkItemCount == WorkList->WorkItemCount) {

            WorkList->State = CompletedWorkList;

            SecondaryStatus = LsapDbLookupSignalCompletionWorkList( WorkList );
        }
    }

    //
    // Done making work list changes
    //

    LsapDbLookupReleaseWorkQueueLock();


    //
    // If necessary, close the Controller Policy Handle.
    //

    if (ControllerPolicyHandle != NULL) {

        SecondaryStatus = LsaClose( ControllerPolicyHandle );
        ControllerPolicyHandle = NULL;
    }

    //
    // If necessary, free the array of Sids looked up at the next level.
    //

    if (NextLevelSids != NULL) {

        MIDL_user_free( NextLevelSids );
        NextLevelSids = NULL;
    }

    //
    // If necessary, free the array of Names looked up at the next level.
    //

    if (NextLevelNames != NULL) {
        MIDL_user_free( NextLevelNames );
        NextLevelNames = NULL;
    }

    if (NextLevelReferencedDomains != NULL) {
        MIDL_user_free( NextLevelReferencedDomains );
        NextLevelReferencedDomains = NULL;
    }

    if (NextLevelTranslatedNames != NULL) {
        MIDL_user_free( NextLevelTranslatedNames );
        NextLevelTranslatedNames = NULL;
    }

    if (NextLevelTranslatedSids != NULL) {
        MIDL_user_free( NextLevelTranslatedSids );
        NextLevelTranslatedSids = NULL;
    }

    return(Status);

LookupProcessWorkItemError:

    goto LookupProcessWorkItemFinish;
}


NTSTATUS
LsapDbLookupSidsUpdateTranslatedNames(
    IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
    IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
    IN PLSA_TRANSLATED_NAME TranslatedNames
    )

/*++

Routine Description:

    This function is called during the processing of a Work Item to update
    the output TranslatedNames and ReferencedDomains parameters of a
    Lookup operation's Work List with the results of a callout to
    LsaICLookupNames.  Zero or more Sids may have been translated.  Note
    that, unlike the translation of Names, Sid translation occurs within
    a single Work Item only.

Arguments:

    WorkList - Pointer to a Work List

    WorkItem - Pointer to a Work Item.

    TranslatedNames - Translated Sids information returned from
        LsaICLookupSids().

Return Values:

    NTSTATUS - Standard Nt Result Code

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG Index, WorkItemIndex;
    BOOLEAN AcquiredWorkQueueLock = FALSE;
    PLSAPR_TRANSLATED_NAME WorkListTranslatedNames =
        WorkList->LookupSidsParams.TranslatedNames->Names;

    //
    // Acquire the Work Queue Lock.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsUpdateTranslatedNamesError;
    }

    AcquiredWorkQueueLock = TRUE;

    for( WorkItemIndex = 0;
         WorkItemIndex < WorkItem->UsedCount;
         WorkItemIndex++) {

        //
        // If this Sid has been fully translated, copy information to output.
        // Note that the Sid is partially translated during the building
        // phase where its Domain is identified.
        //

        if (TranslatedNames[WorkItemIndex].Use != SidTypeUnknown) {

            Index = WorkItem->Indices[WorkItemIndex];

            WorkListTranslatedNames[Index].Use
            = TranslatedNames[WorkItemIndex].Use;

            Status = LsapRpcCopyUnicodeString(
                         NULL,
                         (PUNICODE_STRING) &WorkListTranslatedNames[Index].Name,
                         (PUNICODE_STRING) &TranslatedNames[WorkItemIndex].Name
                         );

            if (!NT_SUCCESS(Status)) {

                break;
            }
        }
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupSidsUpdateTranslatedNamesError;
    }

LookupSidsUpdateTranslatedNamesFinish:

    //
    // If necessary, release the Lookup Work Queue Lock.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupSidsUpdateTranslatedNamesError:

    goto LookupSidsUpdateTranslatedNamesFinish;
}


NTSTATUS
LsapDbLookupNamesUpdateTranslatedSids(
    IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
    IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
    IN PLSA_TRANSLATED_SID TranslatedSids
    )

/*++

Routine Description:

    This function is called during the processing of a Work Item to update
    the output TranslatedSids and ReferencedDomains parameters of a
    Lookup operation's Work List with the results of a callout to
    LsaICLookupNames.  Zero or more Names may have been translated, and
    there is the additional complication of multiple translations of
    Isolated Names as a result of their presence in more than one
    Work Item.  The following rules apply:

    If the Name is a Qualified Name, it only belongs to the specified
    Work Item, so it suffices to check that it has been mapped to a Sid.

    If the Name is an Isolated Name, it belongs to all other Work Items,
    so it may already have been translated during the processing of some
    other Work Item.  If the Name has previously been translated, the prior
    translation stands and the present translation is discarded.  If the
    Name has not previously been translated, the Domain for this Work Item
    is added to the Referenced Domain List and the newly obtained translation
    is stored in the output TranslatedSids array in the Work List.

Arguments:

    WorkList - Pointer to a Work List

    WorkItem - Pointer to a Work Item.  The DomainIndex field will be
        updated if the Domain specified by this Work Item is added to
        the Referenced Domain List by this routine.

    TranslatedSids - Translated Sids information returned from
        LsaICLookupNames().

Return Values:

    NTSTATUS - Standard Nt Result Code

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG WorkItemIndex;
    ULONG Index;
    PLSA_TRANSLATED_SID WorkListTranslatedSids =
        WorkList->LookupNamesParams.TranslatedSids->Sids;
    BOOLEAN AcquiredWorkQueueLock = FALSE;

    //
    // Acquire the Work Queue Lock.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesUpdateTranslatedSidsError;
    }

    AcquiredWorkQueueLock = TRUE;

    for( WorkItemIndex = 0;
         WorkItemIndex < WorkItem->UsedCount;
         WorkItemIndex++) {

        //
        // If this Name has not been translated at all during processing of
        // this Work Item, skip to the next.
        //

        if (LsapDbCompletelyUnmappedSid(&TranslatedSids[WorkItemIndex])) {

            continue;
        }

        //
        // We partially or fully translated the Name during processing of
        // this Work Item.  If this Name has previously been fully translated
        // during the processing of another Work Item, discard the new
        // translation and skip to the next Name.  Note that Qualified
        // Names are always partially translated during the building
        // of the Work List.  Isolated Names are fully translated during
        // the building phase if they are Domain Names.
        //

        Index = WorkItem->Indices[WorkItemIndex];

        if ( WorkListTranslatedSids[ Index ].Use != SidTypeUnknown ) {

            continue;
        }

        //
        // Name has been translated for the first time during the processing
        // of this Work Item.  If this Work Item does not specify a Domain
        // Index, we need to add its Domain to the Referenced Domains List.
        //

        if (WorkItem->DomainIndex == LSA_UNKNOWN_INDEX) {

            Status = LsapDbLookupAddListReferencedDomains(
                         WorkList->ReferencedDomains,
                         &WorkItem->TrustInformation,
                         (PLONG) &WorkItem->DomainIndex
                         );

            if (!NT_SUCCESS(Status)) {

                break;
            }
        }

        //
        // Now update the TranslatedSids array in the Work List.

        WorkListTranslatedSids[Index] = TranslatedSids[WorkItemIndex];
        WorkListTranslatedSids[Index].DomainIndex = WorkItem->DomainIndex;
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupNamesUpdateTranslatedSidsError;
    }

LookupNamesUpdateTranslatedSidsFinish:

    //
    // If necessary, release the Lookup Work Queue Lock.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupNamesUpdateTranslatedSidsError:

    goto LookupNamesUpdateTranslatedSidsFinish;
}


NTSTATUS
LsapDbLookupCreateWorkItem(
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN LONG DomainIndex,
    IN ULONG MaximumEntryCount,
    OUT PLSAP_DB_LOOKUP_WORK_ITEM *WorkItem
    )

/*++

Routine Description:

    This function creates a new Work Item for a name Lookup operation.

Arguments:

    TrustInformation - Specifies the Name of the Trusted Domain
        to which the Work Item relates.  The Sid field may be NULL or
        set to the corresponding Sid.  The Trust Information is expected
        to be in heap or global data.

    DomainIndex - Specifies the Domain Index of this domain relative to
        the Referenced Domain List for the Lookup operation specified
        by the Work List.

    MaximumEntryCount - Specifies the maximum number of entries that
        this Work Item will initialiiy be able to contain.

    WorkItem - Receives a pointer to an empty Work Item structure.

Return Value:

    NTSTATUS - Standard Nt Result Code
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_DB_LOOKUP_WORK_ITEM OutputWorkItem = NULL;
    PULONG OutputIndices = NULL;
    ULONG InitialEntryCount;

    //
    // Allocate memory for the Work Item Header.
    //

    Status = STATUS_INSUFFICIENT_RESOURCES;

    OutputWorkItem = MIDL_user_allocate(sizeof(LSAP_DB_LOOKUP_WORK_ITEM));

    if (OutputWorkItem == NULL) {

        goto LookupCreateWorkItemError;
    }

    RtlZeroMemory(
        OutputWorkItem,
        sizeof(LSAP_DB_LOOKUP_WORK_ITEM)
        );

    //
    // Initialize the fixed fields in the Work Item.
    //

    Status = LsapDbLookupInitializeWorkItem(OutputWorkItem);

    if (!NT_SUCCESS(Status)) {

        goto LookupCreateWorkItemError;
    }

    //
    // Initialize other fields from parameters.
    //

    //
    // Copy the trusted domain information into the work item.  The
    // trust information may be NULL if this is the isolated names
    // work item.
    //

    if (TrustInformation != NULL) {

        OutputWorkItem->TrustInformation.Sid =
            MIDL_user_allocate( RtlLengthSid(TrustInformation->Sid) );

        if (OutputWorkItem->TrustInformation.Sid == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto LookupCreateWorkItemError;
        }

        RtlCopyMemory(
            OutputWorkItem->TrustInformation.Sid,
            TrustInformation->Sid,
            RtlLengthSid(TrustInformation->Sid)
            );

        OutputWorkItem->TrustInformation.Name.MaximumLength = TrustInformation->Name.Length + sizeof(WCHAR);
        OutputWorkItem->TrustInformation.Name.Buffer =
            MIDL_user_allocate(TrustInformation->Name.Length + sizeof(WCHAR));

        if (OutputWorkItem->TrustInformation.Name.Buffer == NULL ) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto LookupCreateWorkItemError;
        }

        RtlCopyUnicodeString(
            (PUNICODE_STRING) &OutputWorkItem->TrustInformation.Name,
            (PUNICODE_STRING) &TrustInformation->Name
            );

    }


    //
    // Create the Indices array in the Work Item.
    //

    InitialEntryCount = (MaximumEntryCount +
                         LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY) &
                         (~LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY);

    Status = STATUS_INSUFFICIENT_RESOURCES;

    OutputIndices = MIDL_user_allocate( InitialEntryCount * sizeof(ULONG) );

    if (OutputIndices == NULL) {

        goto LookupCreateWorkItemError;
    }

    Status = STATUS_SUCCESS;

    OutputWorkItem->UsedCount = (ULONG) 0;
    OutputWorkItem->MaximumCount = InitialEntryCount;
    OutputWorkItem->Indices = OutputIndices;
    OutputWorkItem->DomainIndex = DomainIndex;

LookupCreateWorkItemFinish:

    //
    // Return pointer to newly created Work Item or NULL.
    //

    *WorkItem = OutputWorkItem;
    return(Status);

LookupCreateWorkItemError:

    //
    // Free memory allocated for Indices array.
    //

    if (OutputIndices != NULL) {

        MIDL_user_free( OutputIndices );
        OutputIndices = NULL;
    }

    //
    // Free any memory allocated for the Work Item header.
    //

    if (OutputWorkItem != NULL) {
        if (OutputWorkItem->TrustInformation.Sid != NULL) {
            MIDL_user_free( OutputWorkItem->TrustInformation.Sid );
        }

        if (OutputWorkItem->TrustInformation.Name.Buffer != NULL) {
            MIDL_user_free( OutputWorkItem->TrustInformation.Name.Buffer );
        }

        MIDL_user_free(OutputWorkItem);
        OutputWorkItem = NULL;
    }

    goto LookupCreateWorkItemFinish;
}


NTSTATUS
LsapDbLookupAddIndicesToWorkItem(
    IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
    IN ULONG Count,
    IN PULONG Indices
    )

/*++

Routine Description:

    This function adds an array of Sid or Name indices to a Work Item.
    The indices specify Sids or Names within the Sids or Names arrays in
    the WorkList.  If there is sufficient room in the Work Item's
    existing indices array, the indices will be copied to that array.
    Otherwise, a larger array will be allocated and the existing one
    will be freed after copying the existing indices.

    NOTE:  The Work Item must NOT belong to a Work List that is currently
           on the Work Queue.  The Lookup Work Queue Lock will not be
           taken.
Arguments:

    WorkItem - Pointer to a Work Item structure describing a
        list of Sids or Names and a domain in which they are to be looked up.

    Count - Specifies the number of indices to be added.

    Indices - Specifies the array of indices to be added to the WorkItem.

Return Value:

    NTSTATUS - Standard Nt Result Code
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PULONG OutputIndices = NULL;
    ULONG NewMaximumCount;

    //
    // Check room available in the work item.  If there is enough
    // room, just copy the indices in.
    //

    if (WorkItem->MaximumCount - WorkItem->UsedCount >= Count) {

        RtlCopyMemory(
            &WorkItem->Indices[WorkItem->UsedCount],
            Indices,
            Count * sizeof(ULONG)
            );

        WorkItem->UsedCount += Count;
        goto AddIndicesToWorkItemFinish;
    }

    //
    // Allocate array of sufficient size to accommodate the existing
    // and new indices.  Round up number of entries to some granularity
    // to avoid frequent reallocations.
    //

    Status = STATUS_INSUFFICIENT_RESOURCES;

    NewMaximumCount = ((WorkItem->UsedCount + Count) +
                        LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY) &
                        (~LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY);

    OutputIndices = MIDL_user_allocate( NewMaximumCount * sizeof(ULONG) );

    if (OutputIndices == NULL) {

        goto AddIndicesToWorkItemError;
    }

    Status = STATUS_SUCCESS;

    //
    // Copy in the existing and new indices.
    //

    RtlCopyMemory(
        OutputIndices,
        WorkItem->Indices,
        WorkItem->UsedCount * sizeof(ULONG)
        );

    RtlCopyMemory(
        &OutputIndices[WorkItem->UsedCount],
        Indices,
        Count * sizeof(ULONG)
        );

    //
    // Free the existing indices.  Set pointer to the updated indices array
    // and update the used and maximum counts.
    //

    MIDL_user_free( WorkItem->Indices );
    WorkItem->Indices = OutputIndices;
    WorkItem->UsedCount += Count;
    WorkItem->MaximumCount = NewMaximumCount;

AddIndicesToWorkItemFinish:

    return(Status);

AddIndicesToWorkItemError:

    //
    // Free any memory allocated for the Output Indices array.
    //

    if (OutputIndices != NULL) {

        MIDL_user_free( OutputIndices );
        OutputIndices = NULL;
    }

    goto AddIndicesToWorkItemFinish;
}


NTSTATUS
LsapDbLookupComputeAdvisoryChildThreadCount(
    IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
    )

/*++

Routine Description:

    This function computes the advisory thread count for a Lookup
    operation.  This count is an estimate of the optimal number of
    worker threads (in addition to the main thread) needed to process the
    Work List.

Arguments:

    WorkList - Pointer to a Work List structure describing a
        Lookup Sids or Lookup Names operation.

Return Value:

    NTSTATUS - Standard Nt Result Code
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    ASSERT(WorkList->WorkItemCount != (ULONG) 0);
    WorkList->AdvisoryChildThreadCount = (WorkList->WorkItemCount - (ULONG) 1);

    return(Status);
}


NTSTATUS
LsapDbLookupUpdateAssignableWorkItem(
    IN BOOLEAN MoveToNextWorkList
    )

/*++

Routine Description:

    This function updates the next assignable Work Item pointers.

Arguments:

    MoveToNextWorkList - If TRUE, skip the rest of the current Work List.  If
        FALSE, point at the next item in the current Work List.

Return Value:

    NTSTATUS - Standard Nt Result Code.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_DB_LOOKUP_WORK_ITEM CandAssignableWorkItem = NULL;
    PLSAP_DB_LOOKUP_WORK_LIST CandAssignableWorkList = NULL;
    BOOLEAN AcquiredWorkQueueLock = FALSE;

    //
    // Acquire the LookupWork Queue Lock.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto LookupUpdateAssignableWorkItemError;
    }

    AcquiredWorkQueueLock = TRUE;

    //
    // If there is no Currently Assignable Work List, just exit.
    //

    if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {

        goto LookupUpdateAssignableWorkItemFinish;
    }

    //
    // There is a Currently Assignable Work List.  Unless requested to
    // skip this Work List, examine it.
    //

    if (!MoveToNextWorkList) {

        ASSERT( LookupWorkQueue.CurrentAssignableWorkItem != NULL);

        //
        // Select the next Work Item in the list as candidate for the
        // next Assignable Work Item.  If we have not returned to the First
        // Work Item, selection is complete.
        //

        CandAssignableWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
            LookupWorkQueue.CurrentAssignableWorkItem->Links.Flink;

        if (CandAssignableWorkItem !=
                LookupWorkQueue.CurrentAssignableWorkList->AnchorWorkItem) {

            ASSERT( CandAssignableWorkItem->State == AssignableWorkItem);

            LookupWorkQueue.CurrentAssignableWorkItem = CandAssignableWorkItem;
            goto LookupUpdateAssignableWorkItemFinish;
        }
    }

    //
    // There are no more work items in this Work List or we're to skip the
    // rest of it.  See if there is another Work List.
    //

    CandAssignableWorkList = (PLSAP_DB_LOOKUP_WORK_LIST)
        LookupWorkQueue.CurrentAssignableWorkList->WorkLists.Flink;

    if (CandAssignableWorkList != LookupWorkQueue.AnchorWorkList) {

        //
        // There is another Work List.  Select the first Work Item in the
        // list following the anchor.
        //

        CandAssignableWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
            CandAssignableWorkList->AnchorWorkItem->Links.Flink;

        //
        // Verify that the list does not just contain the Anchor Work Item.
        // Work Lists on the Work Queue should never be empty.
        //

        ASSERT (CandAssignableWorkItem != CandAssignableWorkList->AnchorWorkItem);

        LookupWorkQueue.CurrentAssignableWorkList = CandAssignableWorkList;
        LookupWorkQueue.CurrentAssignableWorkItem = CandAssignableWorkItem;
        goto LookupUpdateAssignableWorkItemFinish;
    }

    //
    // All work has been assigned.  Set pointers to NULL.
    //

    LookupWorkQueue.CurrentAssignableWorkList = NULL;
    LookupWorkQueue.CurrentAssignableWorkItem = NULL;

LookupUpdateAssignableWorkItemFinish:

    //
    // If necessary, release the Lookup Work Queue Lock.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupUpdateAssignableWorkItemError:

    goto LookupUpdateAssignableWorkItemFinish;
}


NTSTATUS
LsapDbLookupStopProcessingWorkList(
    IN PLSAP_DB_LOOKUP_WORK_LIST WorkList,
    IN NTSTATUS TerminationStatus
    )

/*++

Routine Description:

    This function stops further work on a lookup operation at a given
    level and stores an error code.

Arguments:

    WorkList - Pointer to a Work List structure describing a
        Lookup Sids or Lookup Names operation.

    TerminationStatus - Specifies the Nt Result Code to be returned
        by LsarLookupnames or LsarLookupSids.

Return Value:

    NTSTATUS - Standard Nt Result Code
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    BOOLEAN AcquiredWorkQueueLock = FALSE;

    //
    // Acquire the LookupWork Queue Lock.
    //

    Status = LsapDbLookupAcquireWorkQueueLock();

    if (!NT_SUCCESS(Status)) {

        goto LookupStopProcessingWorkListError;
    }

    AcquiredWorkQueueLock = TRUE;

    //
    // Store the termination status in the appropriate WorkList.
    //

    WorkList->Status = TerminationStatus;

    //
    // If this WorkList happens to be the one in which Work Items are being
    // given out, we need to prevent any further Work Items from being given
    // out.  Update the next assignable Work Item pointers, specifying that
    // we should skip to the next Work List (if any).
    //

    if (WorkList == LookupWorkQueue.CurrentAssignableWorkList) {

        Status = LsapDbLookupUpdateAssignableWorkItem(TRUE);
    }

    if (!NT_SUCCESS(Status)) {

        goto LookupStopProcessingWorkListError;
    }

LookupStopProcessingWorkListFinish:

    //
    // If necessary, release the Lookup Work Queue Lock.
    //

    if (AcquiredWorkQueueLock) {

        LsapDbLookupReleaseWorkQueueLock();
        AcquiredWorkQueueLock = FALSE;
    }

    return(Status);

LookupStopProcessingWorkListError:

    goto LookupStopProcessingWorkListFinish;
}


NTSTATUS
LsapRtlExtractDomainSid(
    IN PSID Sid,
    OUT PSID *DomainSid
    )

/*++

Routine Description:

   This function extracts a Domain Sid from a Sid.

Arguments:

    Sid - Pointer to Sid whose Domain Prefix Sid is to be extracted.

    DomainSid - Receives pointer to Domain Sid.  This Sid will be
        allocated memory by MIDL_User_allocate() and should be freed
        via MIDL_user_free when no longer required.

Return Value:

    NTSTATUS - Standard Nt Result Code
--*/

{
    PSID OutputDomainSid;
    ULONG DomainSidLength = RtlLengthSid(Sid) - sizeof(ULONG);



    OutputDomainSid = MIDL_user_allocate( DomainSidLength );
    if (OutputDomainSid == NULL) {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }


    RtlCopyMemory( OutputDomainSid, Sid, DomainSidLength);
    (*(RtlSubAuthorityCountSid(OutputDomainSid)))--;

    *DomainSid = OutputDomainSid;

    return(STATUS_SUCCESS);

}


NTSTATUS
LsapDbLookupInitialize(
    )

/*++

Routine Description:

    This function performs initialization of the data structures
    used by Lookup operations.  These structures are as follows:

    LookupWorkQueue - This is a doubly-linked list of Lookup Work Lists.
        There is one Work List for each Lookup operation in progress
        on a DC.  Each Lookup Work List contains a doubly-linked list
        of Lookup Work Items.  Each Lookup Work Item specifies a
        Trusted Domain and array of Sids or Names to be looked up in that
        domain.  Access to this queue is controlled via the Lookup
        Work Queue Lock.

    Trusted Domain List - This is a doubly-linked list which contains
        the Trust Information (i.e. Domain Sid and Domain Name) of
        each Trusted Domain.  The purpose of this list is to enable
        fast identification of Trusted Domain Sids and Names, without
        having recourse to open or enumerate Trusted Domain objects.
        This list is initialized when the system is loaded, and is
        updated directly when a Trusted Domain object is created or
        deleted.

Arguments:

    None

Return Value:

    NTSTATUS - Standard Nt Result Code.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    //
    // Perform initialization specific to DC's
    //

    if (LsapProductType != NtProductLanManNt) {

        goto LookupInitializeFinish;
    }

    //
    // Create the Lookup Work List initiated event.
    //

    Status = NtCreateEvent(
                 &LsapDbLookupStartedEvent,
                 EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
                 NULL,
                 SynchronizationEvent,
                 FALSE
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupInitializeError;
    }

    //
    // Create the Lookup Work List complete event.
    //

    Status = NtCreateEvent(
                 &LsapDbLookupCompleteEvent,
                 EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
                 NULL,
                 SynchronizationEvent,
                 FALSE
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupInitializeError;
    }

    //
    // Initialize the Lookup Work Queue
    //

    Status = LsapDbLookupInitializeWorkQueue();

    if (!NT_SUCCESS(Status)) {

        goto LookupInitializeError;
    }

LookupInitializeFinish:

    return(Status);

LookupInitializeError:

    goto LookupInitializeFinish;
}


NTSTATUS
LsapDbLookupInitializeWorkQueue(
    )

/*++

Routine Description:

    This function initializes the Lookup Work Queue.  It is only
    called for DC's.

Arguments:

    None

Return Value:

    NTSTATUS - Standard Nt Result Code.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_DB_LOOKUP_WORK_LIST AnchorWorkList = NULL;

    //
    // Initialize the Work Queue Lock.
    //

    Status = RtlInitializeCriticalSection(&LookupWorkQueue.Lock);

    if (!NT_SUCCESS(Status)) {

        LsapLogError(
            "LsapDbLookupInitialize: RtlInit..CritSec returned 0x%lx\n",
            Status
            );
    }

    //
    // Initialize the Work Queue to comprise the Anchor Work List
    // doubly-linked to itself.
    //

    LookupWorkQueue.AnchorWorkList = &LookupWorkQueue.DummyAnchorWorkList;
    AnchorWorkList = &LookupWorkQueue.DummyAnchorWorkList;

    //
    // Set the currently assignable Work List and Work Item pointers to
    // NULL to indicate that there is no work to to.
    //

    LookupWorkQueue.CurrentAssignableWorkList = NULL;
    LookupWorkQueue.CurrentAssignableWorkItem = NULL;

    //
    // Initialize the Anchor Work List.
    //

    Status = LsapDbLookupInitializeWorkList(AnchorWorkList);

    if (!NT_SUCCESS(Status)) {

        goto LookupInitializeWorkQueueError;
    }

    AnchorWorkList->WorkLists.Flink = (PLIST_ENTRY) AnchorWorkList;
    AnchorWorkList->WorkLists.Blink = (PLIST_ENTRY) AnchorWorkList;

    //
    // Set the thread counts.
    //

    LookupWorkQueue.ActiveChildThreadCount = (ULONG) 0;
    LookupWorkQueue.MaximumChildThreadCount = LSAP_DB_LOOKUP_MAX_THREAD_COUNT;
    LookupWorkQueue.MaximumRetainedChildThreadCount = LSAP_DB_LOOKUP_MAX_RET_THREAD_COUNT;

LookupInitializeWorkQueueFinish:

    return(Status);

LookupInitializeWorkQueueError:

    goto LookupInitializeWorkQueueFinish;
}


NTSTATUS
LsapDbLookupInitializeWorkList(
    OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
    )

/*++

Routine Description:

    This function initializes an empty Work List structure.  The Work List
    link fields are not set by this function.

Arguments:

    WorkList - Points to Work List structure to be initialized.

Return Value:

    NTSTATUS - Standard Nt Result Code.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;

    //
    // Initialize miscellaneous fields in the Work List header.
    //

    WorkList->WorkLists.Flink = NULL;
    WorkList->WorkLists.Blink = NULL;
    WorkList->WorkItemCount = (ULONG) 0;
    WorkList->CompletedWorkItemCount = (ULONG) 0;
    WorkList->State = InactiveWorkList;
    WorkList->Status = STATUS_SUCCESS;

    //
    // Initialize the Work List's list of Work Items to comprise the
    // Anchor Work Item doubly-linked to itself.
    //

    WorkList->AnchorWorkItem = &WorkList->DummyAnchorWorkItem;
    AnchorWorkItem = WorkList->AnchorWorkItem;
    AnchorWorkItem->Links.Flink = (PLIST_ENTRY) AnchorWorkItem;
    AnchorWorkItem->Links.Blink = (PLIST_ENTRY) AnchorWorkItem;

    //
    // Initialize the Anchor Work Item.
    //

    Status = LsapDbLookupInitializeWorkItem(AnchorWorkItem);

    if (!NT_SUCCESS(Status)) {

        goto LookupInitializeWorkListError;
    }

LookupInitializeWorkListFinish:

    return(Status);

LookupInitializeWorkListError:

    goto LookupInitializeWorkListFinish;
}


NTSTATUS
LsapDbLookupInitializeWorkItem(
    OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
    )

/*++

Routine Description:

    This function initializes an empty Work Item structure.  The Work Item
    link fields are not set by this function.

Arguments:

    WorkItem - Points to Work Item structure to be initialized.

Return Value:

    NTSTATUS - Standard Nt Result Code.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    WorkItem->UsedCount = (ULONG) 0;
    WorkItem->MaximumCount = (ULONG) 0;
    WorkItem->State = NonAssignableWorkItem;
    WorkItem->Properties = (ULONG) 0;

    return(Status);
}


NTSTATUS
LsapDbLookupLocalDomains(
    OUT PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
    OUT PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
    OUT PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation
    )

/*++

Routine Description:

    This function returns Trust Information for the Local Domains.

Arguments:

    BuiltInDomainTrustInformation - Pointer to structure that will
        receive the Name and Sid of the Built-In Domain.  Unlike
        the other two parameters, the Name and Sid buffers for the
        Built-in Domain are NOT freed after use, because they are
        global data constants.

    AccountDomainTrustInformation - Pointer to structure that will
        receive the Name and Sid of the Accounts Domain.  The Name and
        Sid buffers must be freed after use via MIDL_user_free.

    PrimaryDomainTrustInformation - Pointer to structure that will
        receive the Name and Sid of the Accounts Domain.  The Name and
        Sid buffers must be freed after use via MIDL_user_free.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG WellKnownSidIndex;
    PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;
    PLSAPR_POLICY_PRIMARY_DOM_INFO PolicyPrimaryDomainInfo = NULL;

    //
    // Obtain the Name and Sid of the Built-in Domain
    //

    BuiltInDomainTrustInformation->Sid = LsapBuiltInDomainSid;

    Status = STATUS_INTERNAL_DB_ERROR;

    if (!LsapDbLookupIndexWellKnownSid(
            LsapBuiltInDomainSid,
            (PLSAP_WELL_KNOWN_SID_INDEX) &WellKnownSidIndex
            )) {

        goto LookupLocalDomainsError;
    }

    Status = STATUS_SUCCESS;

    //
    // Obtain the name of the Built In Domain from the table of
    // Well Known Sids.  It suffices to copy the Unicode structures
    // since we do not need a separate copy of the name buffer.
    //

    BuiltInDomainTrustInformation->Name = *((PLSAPR_UNICODE_STRING)
                             LsapDbWellKnownSidName(WellKnownSidIndex));

    //
    // Now obtain the Name and Sid of the Account Domain.
    // The Sid and Name of the Account Domain are both configurable, and
    // we need to obtain them from the Policy Object.  Now obtain the
    // Account Domain Sid and Name by querying the appropriate
    // Policy Information Class.
    //

    Status = LsarQueryInformationPolicy(
                 LsapPolicyHandle,
                 PolicyAccountDomainInformation,
                 (PLSAPR_POLICY_INFORMATION *) &PolicyAccountDomainInfo
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupLocalDomainsError;
    }

    //
    // Set up the Trust Information structure for the Account Domain.
    //

    AccountDomainTrustInformation->Sid = PolicyAccountDomainInfo->DomainSid;

    RtlCopyMemory(
        &AccountDomainTrustInformation->Name,
        &PolicyAccountDomainInfo->DomainName,
        sizeof (UNICODE_STRING)
        );

    //
    // Now obtain the Name and Sid of the Primary Domain (if any)
    // The Sid and Name of the Primary Domain are both configurable, and
    // we need to obtain them from the Policy Object.  Now obtain the
    // Account Domain Sid and Name by querying the appropriate
    // Policy Information Class.
    //

    Status = LsarQueryInformationPolicy(
                 LsapPolicyHandle,
                 PolicyPrimaryDomainInformation,
                 (PLSAPR_POLICY_INFORMATION *) &PolicyPrimaryDomainInfo
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupLocalDomainsError;
    }

    //
    // Set up the Trust Information structure for the Primary Domain.
    //

    PrimaryDomainTrustInformation->Sid = PolicyPrimaryDomainInfo->Sid;

    RtlCopyMemory(
        &PrimaryDomainTrustInformation->Name,
        &PolicyPrimaryDomainInfo->Name,
        sizeof (UNICODE_STRING)
        );

LookupLocalDomainsFinish:

    //
    // If allocated, free up the top-level structures returned by
    // LsarQueryInformationPolicy.
    //

    if (PolicyPrimaryDomainInfo != NULL) {

        MIDL_user_free(PolicyPrimaryDomainInfo);
    }

    if (PolicyAccountDomainInfo != NULL) {

        MIDL_user_free(PolicyAccountDomainInfo);
    }

    return(Status);

LookupLocalDomainsError:

    goto LookupLocalDomainsFinish;
}


NTSTATUS
LsapDbLookupIsolatedDomainName(
    IN ULONG NameIndex,
    IN PLSAPR_UNICODE_STRING IsolatedName,
    IN PLSAPR_TRUST_INFORMATION TrustInformation,
    IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
    IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
    IN OUT PULONG MappedCount,
    IN OUT PULONG CompletelyUnmappedCount,
    IN OUT PULONG MappedIsolatedNameCount
    )

/*++

Routine Description:

    This function translates an Isolated Name if it matches a
    given Domain Name.

Arguments:

    NameIndex - Specifies the index of the entry for the Name within
        the TranslatedSids array, which will be updated if the Name
        matches the Domain Name contained in the TrusteInformation parameter.

    IsolatedName - Specifies the Name to be compared with the Domain Name
        contained in the TrustInformation parameter.

    TrustInformation - Specifies the Name and Sid of a Domain.

    MappedIsolatedNameCount - Pointer to a location in which a count
        of the Isolated Names that have been mapped is maintained.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully.

        STATUS_NONE_MAPPED - The specified name is not the same as the
            name of the specified domain.

        Result codes from called routines.

--*/

{
    NTSTATUS Status = STATUS_NONE_MAPPED;

    //
    // See if the names match.  If they don't, return error.
    //

    if (!RtlEqualDomainName(
            (PUNICODE_STRING) IsolatedName,
            (PUNICODE_STRING) &TrustInformation->Name
            )) {

        goto LookupIsolatedDomainNameError;
    }

    //
    // Name matches the name of the given Domain.  Add that
    // Domain to the Referenced Domain List and translate it.
    //

    Status = LsapDbLookupAddListReferencedDomains(
                 ReferencedDomains,
                 TrustInformation,
                 (PLONG) &TranslatedSids->Sids[NameIndex].DomainIndex
                 );

    if (!NT_SUCCESS(Status)) {

        goto LookupIsolatedDomainNameError;
    }

    //
    // Fill in the Translated Sids entry.
    //

    TranslatedSids->Sids[NameIndex].Use = SidTypeDomain;
    TranslatedSids->Sids[NameIndex].RelativeId = LSA_UNKNOWN_ID;

    Status = STATUS_SUCCESS;
    (*MappedCount)++;
    (*CompletelyUnmappedCount)--;
    (*MappedIsolatedNameCount)++;

LookupIsolatedDomainNameFinish:

    return(Status);

LookupIsolatedDomainNameError:

    goto LookupIsolatedDomainNameFinish;
}

NTSTATUS
LsarGetUserName(
    IN PLSAPR_SERVER_NAME ServerName,
    OUT PLSAPR_UNICODE_STRING * UserName,
    OUT OPTIONAL PLSAPR_UNICODE_STRING * DomainName
    )

/*++

Routine Description:

    This routine is the LSA Server worker routine for the LsaGetUserName
    API.


    WARNING:  This routine allocates memory for its output.  The caller is
    responsible for freeing this memory after use.  See description of the
    Names parameter.

Arguments:

    ServerName - the name of the server the client asked to execute
        this API on, or NULL for the local machine.

    UserName - Receives name of the current user.

    DomainName - Optioanlly receives domain name of the current user.

Return Values:

    NTSTATUS - Standard Nt Result Code

        STATUS_SUCCESS - The call completed successfully and all Sids have
            been translated to names.

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
            such as memory to complete the call.
--*/

{
    LUID LogonId;
    PUNICODE_STRING AccountName;
    PUNICODE_STRING AuthorityName;
    PSID UserSid;
    PSID DomainSid = NULL;
    ULONG Rid;
    PLSAP_LOGON_SESSION LogonSession;
    PTOKEN_USER TokenUserInformation = NULL;
    NTSTATUS Status;
    BOOLEAN LockHeld = FALSE;

    *UserName = NULL;
    if (ARGUMENT_PRESENT(DomainName)) {
        *DomainName = NULL;
    }

    //
    // Let's see if we're trying to look up the currently logged on
    // user.
    //
    //
    // TokenUserInformation from this call must be freed by calling
    // LsapFreeLsaHeap().
    //

    Status = LsapQueryClientInfo(
                &TokenUserInformation,
                &LogonId
                );

    if ( !NT_SUCCESS( Status )) {

        goto Cleanup;
    }

    //
    // If the user ID is Anonymous then there is no name and domain in the
    // logon session
    //

    if (RtlEqualSid(
            TokenUserInformation->User.Sid,
            LsapAnonymousSid
            )) {
        AccountName = &WellKnownSids[LsapAnonymousSidIndex].Name;
        AuthorityName = &WellKnownSids[LsapAnonymousSidIndex].DomainName;

    } else {

        LsapAuLock();
        LockHeld = TRUE;

        LogonSession = LsapGetLogonSession ( &LogonId, FALSE );

        //
        // During setup, we may get NULL returned for the logon session.
        //

        if (LogonSession == NULL) {

            Status = STATUS_NO_SUCH_LOGON_SESSION;
            goto Cleanup;
        }

        //
        // Got a match.  Get the username and domain information
        // from the LogonId
        //


        AccountName   = LogonSession->AccountName;
        AuthorityName = LogonSession->AuthorityName;

    }

    *UserName = MIDL_user_allocate(sizeof(LSAPR_UNICODE_STRING));

    if (*UserName == NULL) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }

    Status = LsapRpcCopyUnicodeString(
                NULL,
                (PUNICODE_STRING) *UserName,
                AccountName
                );

    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }

    //
    // Optionally copy the domain name
    //

    if (ARGUMENT_PRESENT(DomainName)) {

        *DomainName = MIDL_user_allocate(sizeof(LSAPR_UNICODE_STRING));

        if (*DomainName == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto Cleanup;
        }

        Status = LsapRpcCopyUnicodeString(
                    NULL,
                    (PUNICODE_STRING) *DomainName,
                    AuthorityName
                    );

        if (!NT_SUCCESS(Status)) {
            goto Cleanup;
        }

    }




Cleanup:

    if (LockHeld) {
        LsapAuUnlock();
    }

    if (TokenUserInformation != NULL) {
        LsapFreeLsaHeap( TokenUserInformation );
    }

    if (!NT_SUCCESS(Status)) {
        if (*UserName != NULL) {
            if ((*UserName)->Buffer != NULL) {
                MIDL_user_free((*UserName)->Buffer);
            }
            MIDL_user_free(*UserName);
            *UserName = NULL;
        }

        if ( ARGUMENT_PRESENT(DomainName) ){
            if (*DomainName != NULL) {
                if ((*DomainName)->Buffer != NULL) {
                    MIDL_user_free((*DomainName)->Buffer);
                }
                MIDL_user_free(*DomainName);
                *DomainName = NULL;
            }
        }
    }
    return(Status);
}

