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

/*
 * Copyright Likewise Software    2004-2009
 * 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_lookupnames.c
 *
 * Abstract:
 *
 *        Remote Procedure Call (RPC) Server Interface
 *
 *        LsaLookupNames function
 *
 * Authors: Rafal Szczesniak (rafal@likewise.com)
 */

#include "includes.h"


NTSTATUS
LsaSrvLookupNames(
    /* [in] */ handle_t IDL_handle,
    /* [in] */ POLICY_HANDLE hPolicy,
    /* [in] */ UINT32 num_names,
    /* [in] */ UNICODE_STRING *names,
    /* [out] */ RefDomainList **domains,
    /* [in, out] */ TranslatedSidArray *sids,
    /* [in] */ UINT16 level,
    /* [in, out] */ UINT32 *count
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    NTSTATUS ntLookupStatus = STATUS_SUCCESS;
    DWORD dwError = ERROR_SUCCESS;
    PPOLICY_CONTEXT pPolCtx = NULL;
    PWSTR pwszName = NULL;
    DWORD i = 0;
    RefDomainList *pDomains = NULL;
    TranslatedSidArray3 Sids3;
    DWORD dwCount = 0;

    memset(&Sids3, 0, sizeof(Sids3));

    pPolCtx = (PPOLICY_CONTEXT)hPolicy;

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

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

    ntStatus = LsaSrvLookupNamesInternal(
                   IDL_handle,
                   hPolicy,
                   num_names,
                   names,
                   &pDomains,
                   &Sids3,
                   level,
                   &dwCount,
                   0, 0);
    if (ntStatus != STATUS_SUCCESS &&
        ntStatus != STATUS_SOME_NOT_MAPPED &&
        ntStatus != STATUS_NONE_MAPPED)
    {
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntLookupStatus = ntStatus;

    sids->count = Sids3.count;

    ntStatus = LsaSrvAllocateMemory((void**)&(sids->sids),
                                  sizeof(sids->sids[0]) * sids->count);
    BAIL_ON_NT_STATUS(ntStatus);

    for (i = 0; i < sids->count; i++) {
        TranslatedSid *pOut = &(sids->sids[i]);
        TranslatedSid3 *pIn = &(Sids3.sids[i]);
        DWORD iRid = 0;

        pOut->type  = pIn->type;
        pOut->index = pIn->index;

        if (pIn->sid)
        {
            iRid      = pIn->sid->SubAuthorityCount - 1;
            pOut->rid = pIn->sid->SubAuthority[iRid];
        }

    }

    *domains = pDomains;
    *count   = dwCount;

cleanup:

    if (pwszName)
    {
        LW_SAFE_FREE_MEMORY(pwszName);
    }

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

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

    return ntStatus;

error:
    if (pDomains) {
        LsaSrvFreeMemory(pDomains);
    }

    if (sids->sids) {
        LsaSrvFreeMemory(sids->sids);
    }

    *domains    = NULL;
    sids->sids  = NULL;
    sids->count = 0;
    *count      = 0;

    goto cleanup;
}


NTSTATUS
LsaSrvLookupNamesInternal(
    IN  handle_t               hBinding,
    IN  POLICY_HANDLE          hPolicy,
    IN  DWORD                  dwNumNames,
    IN  UNICODE_STRING        *pNames,
    OUT RefDomainList        **ppDomains,
    OUT TranslatedSidArray3   *pSids,
    IN  UINT16                 level,
    OUT PDWORD                 pCount,
    IN  DWORD                  dwUnknown1,
    IN  DWORD                  dwUnknown2
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    NTSTATUS ntLookupStatus = STATUS_SUCCESS;
    DWORD error = ERROR_SUCCESS;
    PPOLICY_CONTEXT pPolCtx = NULL;
    RefDomainList *refDomains = NULL;
    TranslatedSidArray3 SidsArray = {0};
    PSTR* nameStrings = 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_NAME;
    LSA_QUERY_LIST query = {0};
    PLSA_SECURITY_OBJECT* objects = NULL;
    DWORD domainsMaxCount = 0;
    PSID builtinDomainSid = NULL;
    DWORD nameIndex = 0;
    DWORD domainIndex = 0;
    DWORD resolvedCount = 0;
    PSID objectSid = NULL;

    pPolCtx = (PPOLICY_CONTEXT)hPolicy;

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

    if (!(pPolCtx->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 name strings from UNICODE_STRING array argument
    //
    error = LwAllocateMemory(
                   sizeof(*nameStrings) * dwNumNames,
                   OUT_PPVOID(&nameStrings));
    BAIL_ON_LSA_ERROR(error);

    for (nameIndex = 0; nameIndex < dwNumNames; nameIndex++)
    {
        ntStatus = RtlCStringAllocateFromUnicodeString(
                       &nameStrings[nameIndex],
                       &(pNames[nameIndex]));
        BAIL_ON_NT_STATUS(ntStatus);
    }

    query.ppszStrings = (PCSTR*)nameStrings;

    error = LsaSrvFindObjects(
                   lsaConnection,
                   targetProvider,
                   findFlags,
                   objectType,
                   queryType,
                   dwNumNames,
                   query,
                   &objects);
    BAIL_ON_LSA_ERROR(error);

    ntStatus = LsaSrvAllocateMemory(
                   OUT_PPVOID(&SidsArray.sids),
                   sizeof(SidsArray.sids[0]) * dwNumNames);
    BAIL_ON_NT_STATUS(ntStatus);

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

    domainsMaxCount = dwNumNames;

    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 < dwNumNames; nameIndex++)
    {
        if (objects[nameIndex] == NULL)
        {
            SidsArray.sids[nameIndex].type = SID_TYPE_UNKNOWN;
            SidsArray.sids[nameIndex].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)
        {
            ntStatus = RtlAllocateSidFromCString(
                           &objectSid,
                           objects[nameIndex]->pszObjectSid);
            BAIL_ON_NT_STATUS(ntStatus);

            //
            // 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,
                                   objectSid) ||
                    RtlIsPrefixSid(pPolCtx->pLocalDomainSid,
                                   objectSid))
                {
                    SidsArray.sids[nameIndex].type = SID_TYPE_ALIAS;
                }
                else
                {
                    SidsArray.sids[nameIndex].type = SID_TYPE_DOM_GRP;
                }
                break;

            case LSA_OBJECT_TYPE_USER:
                SidsArray.sids[nameIndex].type = SID_TYPE_USER;
                break;

            case LSA_OBJECT_TYPE_WELLKNOWN:
                SidsArray.sids[nameIndex].type = SID_TYPE_WKN_GRP;
                break;
            }

            ntStatus = LsaSrvDuplicateSid(
                           &SidsArray.sids[nameIndex].sid,
                           objectSid);
            BAIL_ON_NT_STATUS(ntStatus);

            // set the domain SID
            objectSid->SubAuthorityCount--;

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

            SidsArray.sids[nameIndex].index = domainIndex;
            SidsArray.sids[nameIndex].unknown1  = 0;

            resolvedCount++;

            RTL_FREE(&objectSid);
        }

        SidsArray.count++;
    }

    if (resolvedCount == 0)
    {
        ntLookupStatus = STATUS_NONE_MAPPED;
    }
    else if (resolvedCount > 0 && resolvedCount < SidsArray.count)
    {
        ntLookupStatus = STATUS_SOME_NOT_MAPPED;
    }
    else if (resolvedCount == SidsArray.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 (SidsArray.sids)
        {
            for (nameIndex = 0;
                 nameIndex < SidsArray.count;
                 nameIndex++)
            {
                LsaSrvFreeMemory(SidsArray.sids[nameIndex].sid);
            }

            LsaSrvFreeMemory(SidsArray.sids);

            SidsArray.sids = NULL;
            SidsArray.count = 0;
        }
    }
    if (lsaConnection)
    {
        LsaSrvCloseServer(lsaConnection);
    }

    for (nameIndex = 0;
         nameStrings && nameIndex < dwNumNames;
         nameIndex++)
    {
        RTL_FREE(&nameStrings[nameIndex]);
    }

    if (objects)
    {
        LsaUtilFreeSecurityObjectList(dwNumNames, objects);
    }

    LW_SAFE_FREE_MEMORY(nameStrings);
    LW_SAFE_FREE_MEMORY(builtinDomainSid);
    RTL_FREE(&objectSid);

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

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

    *ppDomains = refDomains;
    pSids->sids = SidsArray.sids;
    pSids->count = SidsArray.count;
    *pCount = resolvedCount;

    return ntStatus;
}


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