/* 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:
 *
 *        samr_querydomaininfo.c
 *
 * Abstract:
 *
 *        Remote Procedure Call (RPC) Server Interface
 *
 *        SamrQueryDomainInfo function
 *
 * Authors: Rafal Szczesniak (rafal@likewise.com)
 */

#include "includes.h"


static
NTSTATUS
SamrSrvFillDomainInfo1(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo2(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo3(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo4(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo5(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo6(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo7(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo8(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo9(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo11(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo12(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );


static
NTSTATUS
SamrSrvFillDomainInfo13(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    );



NTSTATUS
SamrSrvQueryDomainInfo(
    IN handle_t hBinding,
    IN DOMAIN_HANDLE hDomain,
    IN UINT16 level,
    OUT DomainInfo **ppInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD dwError = ERROR_SUCCESS;
    PDOMAIN_CONTEXT pDomCtx = NULL;
    PCONNECT_CONTEXT pConnCtx = NULL;
    const SAMDB_OBJECT_TYPE ObjectType = SAMDB_OBJECT_TYPE_DOMAIN;
    const SAMDB_QUERY_TYPE QueryType = SAMDB_QUERY_TYPE_SID;
    SAMDB_QUERY Query = {0};
    PSAMDB_OBJECT_INFO *ppObjects = NULL;
    PSAMDB_OBJECT_INFO pObjectInfo = NULL;
    DomainInfo *pInfo = NULL;

    pDomCtx  = (PDOMAIN_CONTEXT)hDomain;
    pConnCtx = pDomCtx->pConnCtx;

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

    if (!(pDomCtx->dwAccessGranted & DOMAIN_ACCESS_LOOKUP_INFO_2))
    {
        ntStatus = STATUS_ACCESS_DENIED;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    Query.Sid = pDomCtx->pDomainSid;
    dwError = SamDbFindObjects(ObjectType,
                               QueryType,
                               1,
                               &Query,
                               &ppObjects);
    BAIL_ON_LSA_ERROR(dwError);

    pObjectInfo = ppObjects[0];
    if (pObjectInfo == NULL)
    {
        ntStatus = STATUS_INVALID_HANDLE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = SamrSrvAllocateMemory(OUT_PPVOID(&pInfo),
                                     sizeof(*pInfo));
    BAIL_ON_NT_STATUS(ntStatus);

    switch (level) {
    case 1:
        ntStatus = SamrSrvFillDomainInfo1(pDomCtx,
                                          pObjectInfo,
                                          pInfo);
        break;

    case 2:
        ntStatus = SamrSrvFillDomainInfo2(pDomCtx,
                                          pObjectInfo,
                                          pInfo);
        break;

    case 3:
        ntStatus = SamrSrvFillDomainInfo3(pDomCtx,
                                          pObjectInfo,
                                          pInfo);
        break;

    case 4:
        ntStatus = SamrSrvFillDomainInfo4(pDomCtx,
                                          pObjectInfo,
                                          pInfo);
        break;

    case 5:
        ntStatus = SamrSrvFillDomainInfo5(pDomCtx,
                                          pObjectInfo,
                                          pInfo);
        break;

    case 6:
        ntStatus = SamrSrvFillDomainInfo6(pDomCtx,
                                          pObjectInfo,
                                          pInfo);
        break;

    case 7:
        ntStatus = SamrSrvFillDomainInfo7(pDomCtx,
                                          pObjectInfo,
                                          pInfo);
        break;

    case 8:
        ntStatus = SamrSrvFillDomainInfo8(pDomCtx,
                                          pObjectInfo,
                                          pInfo);
        break;

    case 9:
        ntStatus = SamrSrvFillDomainInfo9(pDomCtx,
                                          pObjectInfo,
                                          pInfo);
        break;

    case 11:
        ntStatus = SamrSrvFillDomainInfo11(pDomCtx,
                                           pObjectInfo,
                                           pInfo);
        break;

    case 12:
        ntStatus = SamrSrvFillDomainInfo12(pDomCtx,
                                           pObjectInfo,
                                           pInfo);
        break;

    case 13:
        ntStatus = SamrSrvFillDomainInfo13(pDomCtx,
                                           pObjectInfo,
                                           pInfo);
        break;

    default:
        ntStatus = STATUS_INVALID_INFO_CLASS;
        break;
    }

    BAIL_ON_NT_STATUS(ntStatus);

    *ppInfo = pInfo;

cleanup:

    if (ppObjects)
    {
        SamDbFreeObjects(1, ppObjects);
    }
    return ntStatus;

error:
    if (pInfo) {
        SamrSrvFreeMemory(pInfo);
    }

    *ppInfo = NULL;
    goto cleanup;
}


static
NTSTATUS
SamrSrvFillDomainInfo1(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DomainInfo1 *pInfo1 = NULL;
    SAMDB_DOMAIN_INFO *pDomain = NULL;

    pInfo1 = &pInfo->info1;
    pDomain = &pObjectInfo->Info.Domain;

    pInfo1->min_pass_length     = (USHORT)pDomain->MinPasswordLength;
    pInfo1->pass_history_length = (USHORT)pDomain->PasswordHistoryLength;
    pInfo1->pass_properties     = (UINT32)pDomain->PasswordProperties;
    pInfo1->max_pass_age        = pDomain->MaxPasswordAge;
    pInfo1->min_pass_age        = pDomain->MinPasswordAge;

    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo2(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD dwError = ERROR_SUCCESS;
    SAMDB_DOMAIN_INFO *pDomain = NULL;
    SAMDB_ENUM_HANDLE enumHandle = NULL;
    SAMDB_OBJECT_TYPE ObjectType = SAMDB_OBJECT_TYPE_UNKNOWN;
    DomainInfo2 *pInfo2 = NULL;
    DWORD dwNumUsers = 0;
    PSAMDB_OBJECT_INFO* users = NULL;
    PSAMDB_OBJECT_INFO* localGroups = NULL;
    DWORD dwNumAliases = 0;
    WCHAR comment[] = { 0 };

    /*
     * Get the number of user accounts
     */
    ObjectType = SAMDB_OBJECT_TYPE_USER;

    dwError = SamDbOpenEnumObjects(
                         pObjectInfo->Sid,
                         ObjectType,
                         SAMDB_ENUM_SORT_ORDER_NONE,
                         &enumHandle);
    BAIL_ON_LSA_ERROR(dwError);

    dwError = SamDbEnumObjects(
                         enumHandle,
                         DWORD_MAX,
                         &dwNumUsers,
                         &users);
    if (dwError == ERROR_NO_MORE_ITEMS)
    {
        dwError = ERROR_SUCCESS;
    }
    else if (dwError == ERROR_MORE_DATA)
    {
        ntStatus = STATUS_INTERNAL_ERROR;
        LSA_LOG_ERROR("SamDb enumeration returned ERROR_MORE_DATA although "
                      "max count was allowed");
        goto error;
    }
    else if (dwError != ERROR_SUCCESS)
    {
        BAIL_ON_LSA_ERROR(dwError);
    }

    SamDbCloseEnum(enumHandle);
    enumHandle = NULL;

    /*
     * Get the number of Local Group (Alias) accounts
     */
    ObjectType = SAMDB_OBJECT_TYPE_LOCAL_GROUP;
    dwError = SamDbOpenEnumObjects(
                          pObjectInfo->Sid,
                          ObjectType,
                          SAMDB_ENUM_SORT_ORDER_NONE,
                          &enumHandle);
    BAIL_ON_LSA_ERROR(dwError);

    dwError = SamDbEnumObjects(
                          enumHandle,
                          DWORD_MAX,
                          &dwNumAliases,
                          &localGroups);
    if (dwError == ERROR_NO_MORE_ITEMS)
    {
        dwError = ERROR_SUCCESS;
    }
    else if (dwError == ERROR_MORE_DATA)
    {
        ntStatus = STATUS_INTERNAL_ERROR;
        LSA_LOG_ERROR("SamDb enumeration returned ERROR_MORE_DATA although "
                      "max count was allowed");
        goto error;
    }
    else if (dwError != ERROR_SUCCESS)
    {
        BAIL_ON_LSA_ERROR(dwError);
    }

    SamDbCloseEnum(enumHandle);
    enumHandle = NULL;

    pInfo2 = &pInfo->info2;
    pDomain = &pObjectInfo->Info.Domain;

    /* force_logoff_time */
    pInfo2->force_logoff_time = 0;

    /* comment */
    ntStatus = SamrSrvInitUnicodeString(&pInfo2->comment,
                                        comment);
    BAIL_ON_NT_STATUS(ntStatus);

    /* domain_name */
    ntStatus = SamrSrvInitUnicodeString(&pInfo2->domain_name,
                                        pObjectInfo->Name);
    BAIL_ON_NT_STATUS(ntStatus);

    /* primary */
    ntStatus = SamrSrvInitUnicodeString(&pInfo2->primary, NULL);
    BAIL_ON_NT_STATUS(ntStatus);

    /* sequence_num */
    /* unknown1 */

    /* role */
    pInfo2->role = 1;   // report being a domain member

    /* unknown2 */
    /* num_users */
    pInfo2->num_users = dwNumUsers;

    /* num_groups (domain member doesn't support domain groups) */
    pInfo2->num_groups= 0;

    /* num_aliases */
    pInfo2->num_aliases = dwNumAliases;

error:
    if (ntStatus)
    {
        if (pInfo2->comment.Buffer)
        {
            SamrSrvFreeUnicodeString(&pInfo2->comment);
        }

        if (pInfo2->domain_name.Buffer)
        {
            SamrSrvFreeUnicodeString(&pInfo2->domain_name);
        }

        if (pInfo2->primary.Buffer)
        {
            SamrSrvFreeUnicodeString(&pInfo2->primary);
        }

        memset(pInfo2, 0, sizeof(*pInfo2));
    }

    if (users)
    {
        SamDbFreeObjects(dwNumUsers, users);
    }

    if (localGroups)
    {
        SamDbFreeObjects(dwNumAliases, localGroups);
    }

    if (enumHandle)
    {
        SamDbCloseEnum(enumHandle);
    }

    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo3(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DomainInfo3 *pInfo3 = NULL;
    SAMDB_DOMAIN_INFO *pDomain = NULL;

    pInfo3 = &pInfo->info3;
    pDomain = &pObjectInfo->Info.Domain;

    pInfo3->force_logoff_time = 0;

    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo4(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    WCHAR comment[] = { 0 };
    DomainInfo4 *pInfo4 = NULL;
    SAMDB_DOMAIN_INFO *pDomain = NULL;

    pInfo4 = &pInfo->info4;
    pDomain = &pObjectInfo->Info.Domain;


    ntStatus = SamrSrvInitUnicodeString(&pInfo4->comment,
                                        comment);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        if (pInfo4->comment.Buffer)
        {
            SamrSrvFreeUnicodeString(&pInfo4->comment);
        }
        memset(pInfo4, 0, sizeof(*pInfo4));
    }

    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo5(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DomainInfo5 *pInfo5 = NULL;

    pInfo5 = &pInfo->info5;

    ntStatus = SamrSrvInitUnicodeString(&pInfo5->domain_name,
                                        pObjectInfo->Name);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        if (pInfo5->domain_name.Buffer)
        {
            SamrSrvFreeUnicodeString(&pInfo5->domain_name);
        }

        memset(pInfo5, 0, sizeof(*pInfo5));
    }
    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo6(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DomainInfo6 *pInfo6 = NULL;

    pInfo6 = &pInfo->info6;

    ntStatus = SamrSrvInitUnicodeString(&pInfo6->primary,
                                        NULL);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        if (pInfo6->primary.Buffer)
        {
            SamrSrvFreeUnicodeString(&pInfo6->primary);
        }

        memset(pInfo6, 0, sizeof(*pInfo6));
    }

    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo7(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DomainInfo7 *pInfo7 = NULL;
    SAMDB_DOMAIN_INFO *pDomain = NULL;

    pInfo7 = &pInfo->info7;
    pDomain = &pObjectInfo->Info.Domain;

    pInfo7->role = 1;   // report being a domain member

    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo8(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DomainInfo8 *pInfo8 = NULL;
    SAMDB_DOMAIN_INFO *pDomain = NULL;

    pInfo8 = &pInfo->info8;
    pDomain = &pObjectInfo->Info.Domain;

    pInfo8->domain_create_time = 0;

    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo9(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DomainInfo9 *pInfo9 = NULL;

    pInfo9 = &pInfo->info9;
    pInfo9->unknown = 0;

    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo11(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DomainInfo11 *pInfo11 = NULL;
    SAMDB_DOMAIN_INFO *pDomain = NULL;

    pInfo11 = &pInfo->info11;
    pDomain = &pObjectInfo->Info.Domain;

    ntStatus = SamrSrvFillDomainInfo2(pDomCtx,
                                      pObjectInfo,
                                      (DomainInfo*)pInfo11);
    BAIL_ON_NT_STATUS(ntStatus);

    pInfo11->lockout_duration  = pDomain->LockoutDuration;
    pInfo11->lockout_window    = pDomain->LockoutWindow;
    pInfo11->lockout_threshold = pDomain->LockoutThreshold;

error:
    // NOTE: if we add any other failure paths, we'll have to free strings
    //       allocated in SamrSrvFillDomainInfo2()
    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo12(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DomainInfo12 *pInfo12 = NULL;
    SAMDB_DOMAIN_INFO *pDomain = NULL;

    pInfo12 = &pInfo->info12;
    pDomain = &pObjectInfo->Info.Domain;


    pInfo12->lockout_duration  = pDomain->LockoutDuration;
    pInfo12->lockout_window    = pDomain->LockoutWindow;
    pInfo12->lockout_threshold = pDomain->LockoutThreshold;

    return ntStatus;
}


static
NTSTATUS
SamrSrvFillDomainInfo13(
    PDOMAIN_CONTEXT pDomCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    DomainInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DomainInfo13 *pInfo13 = NULL;
    SAMDB_DOMAIN_INFO *pDomain = NULL;

    pInfo13 = &pInfo->info13;
    pDomain = &pObjectInfo->Info.Domain;

    pInfo13->domain_create_time = 0;

    return ntStatus;
}



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