/* Editor Settings: expandtabs and use 4 spaces for indentation
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 */

/*
 * Copyright Likewise Software    2004-2008
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the license, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.  You should have received a copy
 * of the GNU Lesser General Public License along with this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

/*
 * Copyright (C) Likewise Software. All rights reserved.
 *
 * Module Name:
 *
 *        lsa_lookupsids.c
 *
 * Abstract:
 *
 *        Remote Procedure Call (RPC) Server Interface
 *
 *        LsaLookupSids function
 *
 * Authors: Rafal Szczesniak (rafal@likewise.com)
 */

#include "includes.h"


NTSTATUS
LsaSrvLookupSids(
    handle_t hBinding,
    POLICY_HANDLE hPolicy,
    SID_ARRAY *sids,
    RefDomainList **ppDomains,
    TranslatedNameArray *pNames,
    UINT16 level,
    UINT32 *count
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    NTSTATUS ntLookupStatus = STATUS_SUCCESS;
    RefDomainList *pDomains = NULL;
    TranslatedNameArray2 Names = {0};
    DWORD dwCount = 0;
    DWORD i = 0;

    ntStatus = LsaSrvLookupSidsInternal(
                           hBinding,
                           hPolicy,
                           sids,
                           &pDomains,
                           &Names,
                           level,
                           &dwCount,
                           0,
                           0);
    if (ntStatus != STATUS_SUCCESS &&
        ntStatus != STATUS_SOME_NOT_MAPPED &&
        ntStatus != STATUS_NONE_MAPPED)
    {
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntLookupStatus = ntStatus;

    pNames->count = Names.count;
    ntStatus = LsaSrvAllocateMemory(
                           OUT_PPVOID(&pNames->names),
                           sizeof(pNames->names[0]) * pNames->count);
    BAIL_ON_NT_STATUS(ntStatus);

    for (i = 0; i < pNames->count; i++)
    {
        UNICODE_STRING *pIn  = &(Names.names[i].name);
        UNICODE_STRING *pOut = &(pNames->names[i].name);

        pNames->names[i].type      = Names.names[i].type;
        pNames->names[i].sid_index = Names.names[i].sid_index;
        pOut->Buffer               = pIn->Buffer;
        pOut->Length               = pIn->Length;
        pOut->MaximumLength        = pIn->MaximumLength;
    }

error:
    if (ntStatus)
    {
        if (pNames->names)
        {
            for (i = 0; i < pNames->count; i++)
            {
                LsaSrvFreeUnicodeString(&pNames->names[i].name);
            }

            LsaSrvFreeMemory(pNames->names);
            pNames->names = NULL;
            pNames->count = 0;
        }

        pDomains = NULL;
        dwCount  = 0;
    }

    if (Names.names)
    {
        LsaSrvFreeMemory(Names.names);
    }

    if (ntStatus == STATUS_SUCCESS &&
        ntLookupStatus != STATUS_SUCCESS)
    {
        ntStatus = ntLookupStatus;
    }

    *ppDomains = pDomains;
    *count     = dwCount;

    return ntStatus;
}


NTSTATUS
LsaSrvLookupSidsInternal(
    handle_t Binding,
    POLICY_HANDLE LsaPolicy,
    PSID_ARRAY Sids,
    RefDomainList **pDomains,
    TranslatedNameArray2* pNamesArray,
    USHORT Level,
    PDWORD pCount,
    DWORD Unknown1,
    DWORD Unknown2
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    NTSTATUS ntLookupStatus = STATUS_SUCCESS;
    DWORD error = ERROR_SUCCESS;
    PPOLICY_CONTEXT policyContext = NULL;
    RefDomainList* refDomains = NULL;
    TranslatedNameArray2 NamesArray = {0};
    PSTR* sidStrings = NULL;
    HANDLE lsaConnection = NULL;
    PCSTR targetProvider = NULL;
    LSA_FIND_FLAGS findFlags = 0;
    LSA_OBJECT_TYPE objectType = LSA_OBJECT_TYPE_UNDEFINED;
    LSA_QUERY_TYPE queryType = LSA_QUERY_TYPE_BY_SID;
    LSA_QUERY_LIST query = {0};
    PLSA_SECURITY_OBJECT* objects = NULL;
    DWORD domainsMaxCount = 0;
    PSID builtinDomainSid = NULL;
    DWORD sidIndex = 0;
    DWORD nameIndex = 0;
    DWORD domainIndex = 0;
    DWORD resolvedCount = 0;
    PWSTR accountName = NULL;
    PSID domainSid = NULL;

    policyContext = (PPOLICY_CONTEXT)LsaPolicy;

    if (policyContext == NULL || policyContext->Type != LsaContextPolicy)
    {
        ntStatus = STATUS_INVALID_HANDLE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (!(policyContext->dwAccessGranted & LSA_ACCESS_LOOKUP_NAMES_SIDS))
    {
        ntStatus = STATUS_ACCESS_DENIED;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    error = LsaSrvOpenServer(0, 0, getpid(), &lsaConnection);
    BAIL_ON_LSA_ERROR(error);

    //
    // Prepare an array of SID strings from SID_ARRAY argument
    //
    error = LwAllocateMemory(
                   sizeof(*sidStrings) * Sids->dwNumSids,
                   OUT_PPVOID(&sidStrings));
    BAIL_ON_LSA_ERROR(error);

    for (sidIndex = 0; sidIndex < Sids->dwNumSids; sidIndex++)
    {
        ntStatus = RtlAllocateCStringFromSid(
                       &sidStrings[sidIndex],
                       Sids->pSids[sidIndex].pSid);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    query.ppszStrings = (PCSTR*)sidStrings;

    error = LsaSrvFindObjects(
                   lsaConnection,
                   targetProvider,
                   findFlags,
                   objectType,
                   queryType,
                   Sids->dwNumSids,
                   query,
                   &objects);
    BAIL_ON_LSA_ERROR(error);

    ntStatus = LsaSrvAllocateMemory(
                   OUT_PPVOID(&NamesArray.names),
                   sizeof(NamesArray.names[0]) * Sids->dwNumSids);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = LsaSrvAllocateMemory(
                   OUT_PPVOID(&refDomains),
                   sizeof(*refDomains));
    BAIL_ON_NT_STATUS(ntStatus);

    domainsMaxCount = Sids->dwNumSids;

    ntStatus = LsaSrvAllocateMemory(
                   OUT_PPVOID(&refDomains->domains),
                   sizeof(refDomains->domains[0]) * domainsMaxCount);
    BAIL_ON_NT_STATUS(ntStatus);

    refDomains->max_size = domainsMaxCount;

    error = LwAllocateWellKnownSid(
                   WinBuiltinDomainSid,
                   NULL,
                   &builtinDomainSid,
                   NULL);
    BAIL_ON_LSA_ERROR(error);

    for (nameIndex = 0; nameIndex < Sids->dwNumSids; nameIndex++)
    {
        if (objects[nameIndex] == NULL)
        {
            NamesArray.names[nameIndex].type = SID_TYPE_UNKNOWN;
            NamesArray.names[nameIndex].sid_index = 0;
        }
        else if (objects[nameIndex]->type == LSA_OBJECT_TYPE_GROUP ||
                 objects[nameIndex]->type == LSA_OBJECT_TYPE_USER ||
                 objects[nameIndex]->type == LSA_OBJECT_TYPE_WELLKNOWN)
        {
            //
            // Right now we can only tell if it's an alias if the SID
            // prefix is from builtin or our local domain (local
            // accounts database supports aliases only).
            //
            switch (objects[nameIndex]->type)
            {
            case LSA_OBJECT_TYPE_GROUP:
                if (RtlIsPrefixSid(builtinDomainSid,
                                   Sids->pSids[nameIndex].pSid) ||
                    RtlIsPrefixSid(policyContext->pLocalDomainSid,
                                   Sids->pSids[nameIndex].pSid))
                {
                    NamesArray.names[nameIndex].type = SID_TYPE_ALIAS;
                }
                else
                {
                    NamesArray.names[nameIndex].type = SID_TYPE_DOM_GRP;
                }
                break;

            case LSA_OBJECT_TYPE_USER:
                NamesArray.names[nameIndex].type = SID_TYPE_USER;
                break;

            case LSA_OBJECT_TYPE_WELLKNOWN:
                NamesArray.names[nameIndex].type = SID_TYPE_WKN_GRP;
                break;
            }

            error = LwMbsToWc16s(
                               objects[nameIndex]->pszSamAccountName,
                               &accountName);
            BAIL_ON_LSA_ERROR(error);

            ntStatus = LsaSrvInitUnicodeString(
                               &NamesArray.names[nameIndex].name,
                               accountName);
            BAIL_ON_NT_STATUS(ntStatus);

            ntStatus = RtlDuplicateSid(
                               &domainSid,
                               Sids->pSids[nameIndex].pSid);
            BAIL_ON_NT_STATUS(ntStatus);

            domainSid->SubAuthorityCount--;

            error = LsaSrvSetDomainInfo(
                               refDomains,
                               objects[nameIndex]->pszNetbiosDomainName,
                               domainSid,
                               &domainIndex);
            BAIL_ON_LSA_ERROR(error);

            NamesArray.names[nameIndex].sid_index = domainIndex;
            NamesArray.names[nameIndex].unknown1  = 0;

            resolvedCount++;

            LW_SAFE_FREE_MEMORY(accountName);
            RTL_FREE(&domainSid);
        }

        NamesArray.count++;
    }

    if (resolvedCount == 0)
    {
        ntLookupStatus = STATUS_NONE_MAPPED;
    }
    else if (resolvedCount > 0 && resolvedCount < NamesArray.count)
    {
        ntLookupStatus = STATUS_SOME_NOT_MAPPED;
    }
    else if (resolvedCount == NamesArray.count)
    {
        ntLookupStatus = STATUS_SUCCESS;
    }

error:
    if (ntStatus || error)
    {
        if (refDomains)
        {
            if (refDomains->domains)
            {
                for (domainIndex = 0;
                     domainIndex < refDomains->count;
                     domainIndex++)
                {
                    LsaSrvFreeUnicodeString(
                          &refDomains->domains[domainIndex].name);
                    LsaSrvFreeMemory(
                          refDomains->domains[domainIndex].sid);
                }

                LsaSrvFreeMemory(refDomains->domains);
            }

            LsaSrvFreeMemory(refDomains);

            refDomains = NULL;
        }

        if (NamesArray.names)
        {
            for (nameIndex = 0;
                 nameIndex < NamesArray.count;
                 nameIndex++)
            {
                LsaSrvFreeUnicodeString(&NamesArray.names[nameIndex].name);
            }

            LsaSrvFreeMemory(NamesArray.names);

            NamesArray.names = NULL;
            NamesArray.count = 0;
        }
    }

    if (lsaConnection)
    {
        LsaSrvCloseServer(lsaConnection);
    }

    for (sidIndex = 0;
         sidStrings && sidIndex < Sids->dwNumSids;
         sidIndex++)
    {
        RTL_FREE(&sidStrings[sidIndex]);
    }

    if (objects)
    {
        LsaUtilFreeSecurityObjectList(Sids->dwNumSids, objects);
    }

    LW_SAFE_FREE_MEMORY(sidStrings);
    LW_SAFE_FREE_MEMORY(accountName);
    RTL_FREE(&domainSid);
    LW_SAFE_FREE_MEMORY(builtinDomainSid);

    if (ntStatus == STATUS_SUCCESS &&
        error != ERROR_SUCCESS)
    {
        ntStatus = LwWin32ErrorToNtStatus(error);
    }

    if (ntStatus == STATUS_SUCCESS)
    {
        ntStatus = ntLookupStatus;
    }

    *pDomains          = refDomains;
    pNamesArray->names = NamesArray.names;
    pNamesArray->count = NamesArray.count;
    *pCount            = resolvedCount;

    return ntStatus;
}


NTSTATUS
LsaSrvSetDomainInfo(
    RefDomainList* Domains,
    PSTR DomainName,
    PSID DomainSid,
    PDWORD DomainIndex
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD error = ERROR_SUCCESS;
    DWORD domainIndex = 0;
    PWSTR pwszName = NULL;

    //
    // Search for domain in the current list
    //
    for (domainIndex = 0; domainIndex < Domains->count; domainIndex++)
    {
        if (RtlEqualSid(DomainSid, Domains->domains[domainIndex].sid))
        {
            break;
        }
    }

    //
    // No domain found in the list so we have to add it
    //
    if (domainIndex == Domains->count)
    {
        LsaDomainInfo* info = &Domains->domains[domainIndex];

        error = LwMbsToWc16s(DomainName,
                             &pwszName);
        BAIL_ON_LSA_ERROR(error);

        ntStatus = LsaSrvInitUnicodeString(
                               &info->name,
                               pwszName);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = LsaSrvDuplicateSid(
                               &info->sid,
                               DomainSid);
        BAIL_ON_NT_STATUS(ntStatus);

        Domains->count++;
        LSA_ASSERT(!(Domains->count > Domains->max_size));
    }

error:
    LW_SAFE_FREE_MEMORY(pwszName);

    *DomainIndex = domainIndex;

    if (ntStatus == STATUS_SUCCESS &&
        error != ERROR_SUCCESS)
    {
        ntStatus = LwWin32ErrorToNtStatus(error);
    }

    return ntStatus;
}



/*
local variables:
mode: c
c-basic-offset: 4
indent-tabs-mode: nil
tab-width: 4
end:
*/
