/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    dsgroups.cpp

Abstract:

    Routines to configure/analyze groups in DS

Author:

    Jin Huang (jinhuang) 7-Nov-1996

--*/
#include "headers.h"
#include "serverp.h"
#include <io.h>
#include <lm.h>
#include <lmcons.h>
#include <lmapibuf.h>
#pragma hdrstop

//
// LDAP handle
//
PLDAP Thread pGrpLDAP = NULL;
HANDLE Thread hDS = NULL;

HINSTANCE Thread hNtdsApi = NULL;

#define SCEGRP_MEMBERS      1
#define SCEGRP_MEMBERSHIP   2

#if _WIN32_WINNT>=0x0500

typedef DWORD (WINAPI *PFNDSBIND) (TCHAR *, TCHAR *, HANDLE *);
typedef DWORD (WINAPI *PFNDSUNBIND) (HANDLE *);
typedef DWORD (WINAPI *PFNDSCRACKNAMES) ( HANDLE, DS_NAME_FLAGS, DS_NAME_FORMAT, \
                              DS_NAME_FORMAT, DWORD, LPTSTR *, PDS_NAME_RESULT *);
typedef void (WINAPI *PFNDSFREENAMERESULT) (DS_NAME_RESULT *);


DWORD
ScepDsConfigGroupMembers(
    IN PSCE_OBJECT_LIST pRoots,
    IN PWSTR GroupName,
    IN OUT DWORD *pStatus,
    IN PSCE_NAME_LIST pMembers,
    IN PSCE_NAME_LIST pMemberOf,
    IN OUT DWORD *nGroupCount
    );

DWORD
ScepDsGetDsNameList(
    IN PSCE_NAME_LIST pNameList,
    OUT PSCE_NAME_LIST *pRealNames
    );

DWORD
ScepDsCompareNames(
    IN PWSTR *Values,
    IN OUT PSCE_NAME_LIST *pAddList,
    OUT PSCE_NAME_LIST *pDeleteList OPTIONAL
    );

DWORD
ScepDsChangeMembers(
    IN ULONG Flag,
    IN PWSTR RealGroupName,
    IN PSCE_NAME_LIST pAddList OPTIONAL,
    IN PSCE_NAME_LIST pDeleteList OPTIONAL
    );

DWORD
ScepDsAnalyzeGroupMembers(
    IN LSA_HANDLE LsaPolicy,
    IN PSCE_OBJECT_LIST pRoots,
    IN PWSTR GroupName,
    IN PWSTR KeyName,
    IN DWORD KeyLen,
    IN OUT DWORD *pStatus,
    IN PSCE_NAME_LIST pMembers,
    IN PSCE_NAME_LIST pMemberOf,
    IN OUT DWORD *nGroupCount
    );

DWORD
ScepDsMembersDifferent(
    IN ULONG Flag,
    IN PWSTR *Values,
    IN OUT PSCE_NAME_LIST *pNameList,
    OUT PSCE_NAME_LIST *pCurrentList,
    OUT PBOOL pbDifferent
    );

PWSTR
ScepGetLocalAdminsName();

DWORD
ScepDsConvertDsNameList(
    IN OUT PSCE_NAME_LIST pDsNameList
    );

//
// helpers
//

SCESTATUS
ScepCrackOpen(
    OUT HANDLE *phDS
    )
{

    if ( !phDS ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    DWORD               Win32rc;

    *phDS = NULL;

    hNtdsApi = LoadLibrary(TEXT("ntdsapi.dll"));

    if ( hNtdsApi == NULL ) {
        return (SCESTATUS_MOD_NOT_FOUND);
    }

    PFNDSBIND pfnDsBind;
    PFNDSUNBIND pfnDsUnBind;

#if defined(UNICODE)
    pfnDsBind = (PFNDSBIND)GetProcAddress(hNtdsApi, "DsBindW");
    pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindW");
#else
    pfnDsBind = (PFNDSBIND)GetProcAddress(hNtdsApi, "DsBindA");
    pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindA");
#endif

    if ( pfnDsBind == NULL || pfnDsUnBind == NULL ) {
        return(SCESTATUS_MOD_NOT_FOUND);
    }


    Win32rc = (*pfnDsBind) (
                NULL,
                NULL,
                phDS);


    if ( Win32rc != ERROR_SUCCESS ) {
        ScepLogOutput3(3, Win32rc, IDS_ERROR_BIND, L"the GC");
    }

    return(ScepDosErrorToSceStatus(Win32rc));

}

SCESTATUS
ScepCrackClose(
    IN HANDLE *phDS
    )
{
    if ( hNtdsApi ) {

        if ( phDS ) {

            PFNDSUNBIND pfnDsUnBind;

#if defined(UNICODE)
            pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindW");
#else
            pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindA");
#endif

            if ( pfnDsUnBind ) {
                (*pfnDsUnBind) (phDS);
            }
        }

        FreeLibrary(hNtdsApi);
        hNtdsApi = NULL;

    }

    return(SCESTATUS_SUCCESS);
}
#endif

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Functions to configure group membership in DS
//
//
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

SCESTATUS
ScepConfigDsGroups(
    IN OUT PSCE_GROUP_MEMBERSHIP pGroupMembership,
    IN DWORD ConfigOptions
    )
/* ++

Routine Description:

   Configure the ds group membership. The main difference of ds groups
   from NT4 groups is that now group can be a member of another group.
   Members in the group are configured exactly as the pMembers list in
   the restricted group. The group is only validated (added) as a member
   of the MemberOf group list. Other existing members in those groups
   won't be removed.

   The restricted groups are specified in the SCP profile by group name.
   It could be a global group, or a alias (no difference in NT5 DS),
   but must be defined in the local domain.

Arguments:

    pGroupMembership - The restricted group list with members/memberof info to configure

    ConfigOptions - options passed in for the configuration

Return value:

   SCESTATUS error codes

++ */
{

#if _WIN32_WINNT<0x0500
    return(SCESTATUS_SUCCESS);

#else
    if ( pGroupMembership == NULL ) {

        ScepPostProgress(TICKS_GROUPS,
                         AREA_GROUP_MEMBERSHIP,
                         NULL);

        return(SCESTATUS_SUCCESS);
    }

    SCESTATUS           rc;

    //
    // open the Ldap server, should open two ldap server, one for the local domain
    // the other is for the global search (for members, membership)
    //

    rc = ScepLdapOpen(&pGrpLDAP);

    if ( rc == SCESTATUS_SUCCESS ) {
        rc = ScepCrackOpen(&hDS);
    }

    if ( rc == SCESTATUS_SUCCESS ) {

        //
        // get the root of the domain
        //

        PSCE_OBJECT_LIST pRoots=NULL;

        rc = ScepEnumerateDsObjectRoots(
                    pGrpLDAP,
                    &pRoots
                    );

        if ( rc == SCESTATUS_SUCCESS ) {

            PSCE_GROUP_MEMBERSHIP pGroup;
            DWORD               Win32rc;
            DWORD               rc32=NO_ERROR;  // the saved status
            BOOL                bAdminFound=FALSE;
            DWORD               nGroupCount=0;

            //
            // configure each group
            //

            for ( pGroup=pGroupMembership; pGroup != NULL; pGroup=pGroup->Next ) {

                //
                // if both members and memberof are not defined for the group
                // we don't need to do anything for the group
                //

                if ( ( pGroup->Status & SCE_GROUP_STATUS_NC_MEMBERS ) &&
                     ( pGroup->Status & SCE_GROUP_STATUS_NC_MEMBEROF  ) ) {
                    continue;
                }

                //
                // if within policy propagation and a system shutdown
                // is requested, we need to quit as soon as possible
                //

                if ( (ConfigOptions & SCE_POLICY_TEMPLATE) &&
                     ScepIsSystemShutDown() ) {

                    rc = SCESTATUS_SERVICE_NOT_SUPPORT;
                    break;
                }

                LPTSTR pTemp = wcschr(pGroup->GroupName, L'\\');
                if ( pTemp ) {

                    //
                    // there is a domain name, check it with computer name
                    // to determine if the account is local
                    //

                    UNICODE_STRING uName;

                    uName.Buffer = pGroup->GroupName;
                    uName.Length = ((USHORT)(pTemp-pGroup->GroupName))*sizeof(TCHAR);

                    if ( !ScepIsDomainLocal(&uName) ) {

                        //
                        // non local groups are not supported for the configuration
                        //

                        ScepLogOutput3(1, 0, SCEDLL_NO_MAPPINGS, pGroup->GroupName);
                        rc = SCESTATUS_INVALID_DATA;

                        pGroup->Status |= SCE_GROUP_STATUS_DONE_IN_DS;
                        continue;
                    }
                    pTemp++;

                } else {

                    pTemp = pGroup->GroupName;
                }

                //
                // local groups will be handled outside (in SAM)
                // find the group (validate) in this domain
                //

                Win32rc = ScepDsConfigGroupMembers(
                                         pRoots,
                                         pTemp, // pGroup->GroupName,
                                         &(pGroup->Status),
                                         pGroup->pMembers,
                                         pGroup->pMemberOf,
                                         &nGroupCount
                                         );

                if ( Win32rc != ERROR_SUCCESS &&
                     (pGroup->Status & SCE_GROUP_STATUS_DONE_IN_DS) ) {

                    //
                    // the group should be handled by the DS function
                    // but it failed.
                    //

                    ScepLogOutput3(1,Win32rc, SCEDLL_SCP_ERROR_CONFIGURE, pGroup->GroupName);

                    rc32 = Win32rc;

                    if ( Win32rc == ERROR_FILE_NOT_FOUND ||
                         Win32rc == ERROR_SHARING_VIOLATION ||
                         Win32rc == ERROR_ACCESS_DENIED ) {

                        Win32rc = ERROR_SUCCESS;

                    } else
                        break;
                }

            }

            if ( rc32 != NO_ERROR ) {
                rc = ScepDosErrorToSceStatus(rc32);
            }

            //
            // free the root DN buffer
            //

            ScepFreeObjectList(pRoots);

        }

    }

    if ( pGrpLDAP ) {
        ScepLdapClose(&pGrpLDAP);
        pGrpLDAP = NULL;
    }

    ScepCrackClose(&hDS);
    hDS = NULL;

    //
    // ticks will be called within ConfigureGroupMembership, so ignore it here
    //

    return(rc);
#endif

}


#if _WIN32_WINNT>=0x0500

DWORD
ScepDsConfigGroupMembers(
    IN PSCE_OBJECT_LIST pRoots,
    IN PWSTR GroupName,
    IN OUT DWORD *pStatus,
    IN PSCE_NAME_LIST pMembers,
    IN PSCE_NAME_LIST pMemberOf,
    IN OUT DWORD *nGroupCount
    )
/*
Description:

    Configure group membership (members and Memberof) of a group, specified by
    GroupName.

    The group membership is configured using ldap based on info stored in DS.
    Since foreign wellknown principals may not be present in the Active
    Directory, this function cannot configure membership with well known
    principals.

    Global groups and Universal groups cannot have wellknown pricipals as
    members (or memberof) but local groups (such as builtin groups) can. In
    order to solve this problem, local groups are configured using the old
    SAM apis outside of this function. This function only configures global
    and Universal group defined in the local domain. If the group is a
    global or universal group, the pStatus parameter will be marked to
    indicate the group is processed by this function (SCE_GROUP_STATUS_DONE_IN_DS)
    so the old SAM function can skip it.

Arguments:

    pRoots      - contains the local domain's base DN

    GroupName   - the group name to configure

    pStatus     - the status of the group (such as member defined, memberof defined, etc)

    pMembers    - the list of members to configure

    pMemberOf   - the list of memberOf to configure

    nGroupCount - the count maintained for progress indication only. If the group is
                  processed in this function, the count will be incremented.

Return:

    WIN32 error code.

*/
{
    if ( GroupName == NULL ) {
        return(ERROR_SUCCESS);
    }

    if ( pRoots == NULL ) {
        return(ERROR_INVALID_PARAMETER);
    }

    DWORD retErr = ERROR_SUCCESS;
    DWORD retSave = ERROR_SUCCESS;

    //
    // search for the group name, if find it, get members and memberof attributes
    //

    LDAPMessage *Message = NULL;
    PWSTR    Attribs[4];

    Attribs[0] = L"distinguishedName";
    Attribs[1] = L"member";
    Attribs[2] = L"memberOf";
    Attribs[3] = NULL;

    WCHAR tmpBuf[128];

    //
    // define a filter for global or universal group only
    //

    wcscpy(tmpBuf, L"( &(&(|");
    swprintf(tmpBuf+wcslen(L"( &(&(|"), L"(groupType=%d)(groupType=%d))(objectClass=group))(samAccountName=\0",
             GROUP_TYPE_ACCOUNT_GROUP | GROUP_TYPE_SECURITY_ENABLED, GROUP_TYPE_UNIVERSAL_GROUP | GROUP_TYPE_SECURITY_ENABLED);

    PWSTR Filter;

    Filter = (PWSTR)LocalAlloc(LMEM_ZEROINIT,
                               (wcslen(tmpBuf)+wcslen(GroupName)+4)*sizeof(WCHAR));

    if ( Filter == NULL ) {
        return(SCESTATUS_NOT_ENOUGH_RESOURCE);
    }

    swprintf(Filter, L"%s%s) )", tmpBuf, GroupName);

    //
    // no chased referrel search because the group must be defined locally
    // on the domain
    //

    pGrpLDAP->ld_options = 0;

    retErr = ldap_search_s(
              pGrpLDAP,
              pRoots->Name,
              LDAP_SCOPE_SUBTREE,
              Filter,
              Attribs,
              0,
              &Message);

    retErr = LdapMapErrorToWin32(retErr);

    if(retErr == ERROR_SUCCESS) {

        LDAPMessage *Entry = NULL;

        //
        // find the group, should have only one entry, unless there are duplicate
        // groups within the domain, in which case, we only care the first entry anyway
        //

        Entry = ldap_first_entry(pGrpLDAP, Message);

        if(Entry != NULL) {

            //
            // get the values of requested attributes
            // Note, Value pointer returned must be freed
            //

            PWSTR  *Values;
            PWSTR  RealGroupName;

            //
            // the DN name
            //

            Values = ldap_get_values(pGrpLDAP, Entry, Attribs[0]);

            if(Values != NULL) {

                ScepLogOutput3(1,0, SCEDLL_SCP_CONFIGURE, GroupName);

                if ( *nGroupCount < TICKS_GROUPS ) {
                    ScepPostProgress(1,
                                     AREA_GROUP_MEMBERSHIP,
                                     GroupName);
                    *nGroupCount++;
                }

                //
                // Save the real group name for add/remove members later.
                //

                RealGroupName = (PWSTR)LocalAlloc(0,(wcslen(Values[0]) + 1)*sizeof(WCHAR));
                if ( RealGroupName != NULL ) {

                    wcscpy(RealGroupName, Values[0]);
                    ldap_value_free(Values);

                    ScepLogOutput3(3, 0, SCEDLL_SCP_CONFIGURE, RealGroupName);

                    PSCE_NAME_LIST pRealNames=NULL;
                    PSCE_NAME_LIST pDeleteNames=NULL;

                    //
                    // translate each name in the pMembers list to real ds names (search)
                    //

                    if ( !( *pStatus & SCE_GROUP_STATUS_NC_MEMBERS) ) {

                        retErr = ScepDsGetDsNameList(pMembers, &pRealNames);

                        retSave = retErr;

                        //
                        // continue to configure group membership even if
                        // there are some members not resolved
                        //
                        // BUT if no member is resolved, do not proceed to remove
                        // all members
                        //

                        if ( retErr == ERROR_SUCCESS ||
                             (retErr == ERROR_FILE_NOT_FOUND && pRealNames) ) {

                            //
                            // get members attribute
                            //

                            Values = ldap_get_values(pGrpLDAP, Entry, Attribs[1]);

                            if ( Values != NULL ) {

                                //
                                // process each member
                                //

                                retErr = ScepDsCompareNames(Values, &pRealNames, &pDeleteNames);
                                ldap_value_free(Values);

                            } else {
                                //
                                // it is OK if no members are found
                                //
                                ScepLogOutput3(3, 0, SCEDLL_EMPTY_MEMBERSHIP);
                                retErr = ERROR_SUCCESS;
                            }

                            if ( NO_ERROR == retErr ) {

                                //
                                // add/remove members of the group
                                //

                                retErr = ScepDsChangeMembers(SCEGRP_MEMBERS,
                                                             RealGroupName,
                                                             pRealNames,
                                                             pDeleteNames);
                            }

                            if ( ERROR_SUCCESS == retSave ) {
                                retSave = retErr;
                            }
                        }

                        //
                        // free buffers
                        //

                        ScepFreeNameList(pRealNames);
                        ScepFreeNameList(pDeleteNames);
                        pRealNames = NULL;
                        pDeleteNames = NULL;
                    }

                    if ( !( *pStatus & SCE_GROUP_STATUS_NC_MEMBEROF) ) {

                        //
                        // memberof is also defined for the group
                        // crack the memberof list first
                        //

                        retErr = ScepDsGetDsNameList(pMemberOf, &pRealNames);

                        if ( ERROR_SUCCESS == retSave ) {
                            retSave = retErr;
                        }

                        if ( ( ERROR_SUCCESS == retErr ||
                               ERROR_FILE_NOT_FOUND == retErr ) && pRealNames ) {

                            //
                            // get memberof attribute of the group
                            //

                            Values = ldap_get_values(pGrpLDAP, Entry, Attribs[2]);

                            if ( Values != NULL ) {

                                //
                                // process each membership
                                //

                                retErr = ScepDsCompareNames(Values, &pRealNames, NULL);
                                ldap_value_free(Values);

                            } else {

                                //
                                // it is OK if no membership is defined
                                //

                                ScepLogOutput3(3, 0, SCEDLL_EMPTY_MEMBERSHIP);
                                retErr = NO_ERROR;
                            }

                            if ( retErr == NO_ERROR ) {

                                //
                                // add the group to the defined membership
                                // Note, other existing membership is not removed
                                //

                                retErr = ScepDsChangeMembers(SCEGRP_MEMBERSHIP,
                                                             RealGroupName,
                                                             pRealNames,
                                                             NULL);
                            }

                            ScepFreeNameList(pRealNames);
                            pRealNames = NULL;

                            //
                            // remember the error
                            //

                            if ( ERROR_SUCCESS == retSave ) {
                                retSave = retErr;
                            }
                        }

                    }

                    LocalFree(RealGroupName);

                } else {
                    ldap_value_free(Values);
                    retErr = ERROR_NOT_ENOUGH_MEMORY;
                }

                //
                // regardless success or failure, this group has been
                // processed by this function. Mark it so that it will
                // be skipped by the old SAM API
                //

                *pStatus |= SCE_GROUP_STATUS_DONE_IN_DS;

            } else {

                //
                // Value[0] (group name) can not be empty
                //

                retErr = LdapMapErrorToWin32(pGrpLDAP->ld_errno);
                ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
            }

        } else {

            //
            // the group is not found
            //

            retErr = ERROR_FILE_NOT_FOUND;
            ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
        }

    } else {

        //
        // error finding the group (with the filter defined)
        //

        ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, Filter);
    }

    //
    // free Filter
    //

    if ( Message )
        ldap_msgfree(Message);

    LocalFree(Filter);

    //
    // return the error
    //

    if ( ERROR_SUCCESS == retSave ) {
        retSave = retErr;
    }

    return(retSave);
}


DWORD
ScepDsGetDsNameList(
    IN PSCE_NAME_LIST pNameList,
    OUT PSCE_NAME_LIST *pRealNames
    )
/*
Description:

    Translate account names in the list to FQDN format (CN=<account>,DC=<domain>,...).
    The output list pRealNames can be filled up even if the function returns
    error, to handle valid accounts while there are invalid accounts defined in
    the list.

Arguments:

    pNameList - the link list for accounts in name format to convert

    pRealNames - the output link list for converted FQDN format accounts

Return:

    WIN32 error code.

    If ERROR_FILE_NOT_FOUND is returned, it means that some accounts in the
    input list cannot be cracked.

*/
{

    if ( pNameList == NULL ) {
        return(ERROR_SUCCESS);
    }

    if ( pRealNames == NULL ) {
        return(ERROR_INVALID_PARAMETER);
    }

    //
    // find the procedure address of DsCrackNames and DsFreeNameResult
    // ntdsapi.dll is dynamically loaded in ScepCrackOpen
    //

    PFNDSCRACKNAMES pfnDsCrackNames=NULL;
    PFNDSFREENAMERESULT pfnDsFreeNameResult=NULL;

    if ( hNtdsApi ) {

#if defined(UNICODE)
        pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesW");
        pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultW");
#else
        pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesA");
        pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultA");
#endif
    }

    //
    // the two entry points must exist before continue
    //

    if ( pfnDsCrackNames == NULL || pfnDsFreeNameResult == NULL ) {
        return(ERROR_PROC_NOT_FOUND);
    }

    DWORD retErr=ERROR_SUCCESS;
    DWORD retSave=ERROR_SUCCESS;
    PWSTR pTemp;
    DS_NAME_RESULT *pDsResult=NULL;

    //
    // loop through each name in the list to crack
    //

    for ( PSCE_NAME_LIST pName = pNameList; pName != NULL; pName = pName->Next ) {

        //
        // Crack the name from NT4 account name to FQDN. Note, hDS is bound to
        // the GC in order to crack foreign domain accounts
        //

        retErr = (*pfnDsCrackNames) (
                        hDS,                // in
                        DS_NAME_FLAG_TRUST_REFERRAL,  // in
                        DS_NT4_ACCOUNT_NAME,// in
                        DS_FQDN_1779_NAME,  // in
                        1,                  // in
                        &(pName->Name),     // in
                        &pDsResult);        // out

        if(retErr == ERROR_SUCCESS && pDsResult &&
            pDsResult->cItems > 0 && pDsResult->rItems ) {

            if ( pDsResult->rItems[0].pName ) {

                //
                // find the member
                // Save the real group name for add/remove members later.
                //

                ScepLogOutput3(3,0, SCEDLL_PROCESS, pDsResult->rItems[0].pName);

                retErr = ScepAddToNameList(pRealNames, pDsResult->rItems[0].pName, 0);

            } else {

                //
                // this name cannot be cracked.
                //

                retErr = pDsResult->rItems[0].status;
                ScepLogOutput3(1,retErr, SCEDLL_CANNOT_FIND_INDS, pName->Name);

            }

        } else {

            //
            // no match is found
            //

            retErr = ERROR_FILE_NOT_FOUND;
            ScepLogOutput3(1,retErr, SCEDLL_CANNOT_FIND_INDS, pName->Name);

        }

        if ( pDsResult ) {
            (*pfnDsFreeNameResult) (pDsResult);
            pDsResult = NULL;
        }

        //
        // remember the error to return
        //

        if ( ERROR_SUCCESS != retErr )
            retSave = retErr;

    }

    return(retSave);
}


DWORD
ScepDsCompareNames(
    IN PWSTR *Values,
    IN OUT PSCE_NAME_LIST *pAddList,
    OUT PSCE_NAME_LIST *pDeleteList OPTIONAL
    )
/*
Description:


Arguments:

    Values

    pAddList

    pDeleteList

Return Value:

    WIN32 error
*/
{
    if ( Values == NULL || pAddList == NULL ) {
        return(ERROR_INVALID_PARAMETER);
    }

    //
    // count how many existing members (memberof)
    //

    ULONG ValCount = ldap_count_values(Values);

    DWORD rc=NO_ERROR;
    PSCE_NAME_LIST pTemp;

    //
    // loop through each existing value to compare with the ones defined
    // for configuration to determine which one should be added and which
    // one should be removed from the membership
    //

    for(ULONG index = 0; index < ValCount; index++) {

        if ( Values[index] == NULL ) {
            continue;
        }

        pTemp = *pAddList;
        PSCE_NAME_LIST pParent = NULL, pTemp2;
        BOOL bFound=FALSE;

        while (pTemp != NULL ) {

            if ( _wcsicmp(Values[index], pTemp->Name) == 0 ) {

                //
                // find this member in both place, no need to add or remove
                // from the membership so take this one out of the list
                //

                if ( pParent == NULL ) {
                    *pAddList = pTemp->Next;
                } else
                    pParent->Next = pTemp->Next;

                pTemp2 = pTemp;
                pTemp = pTemp->Next;

                pTemp2->Next = NULL;
                ScepFreeNameList(pTemp2);

                bFound=TRUE;

                break;

            } else {

                //
                // move to the next one
                //

                pParent = pTemp;
                pTemp = pTemp->Next;
            }
        }

        if ( !bFound && pDeleteList != NULL ) {

            //
            // did not find in the real name list, should be deleted
            // if the remove buffer is passed in
            //

            rc = ScepAddToNameList(pDeleteList, Values[index], 0);

            if ( rc != ERROR_SUCCESS ) {
                ScepLogOutput3(1,rc, SCEDLL_SCP_ERROR_ADD, Values[index]);
            }
        }

        if ( rc != NO_ERROR ) {

            //
            // pDeleteList will be freed outside
            //

            break;
        }
    }

    return(rc);

}


DWORD
ScepDsChangeMembers(
    IN ULONG Flag,
    IN PWSTR RealGroupName,
    IN PSCE_NAME_LIST pAddList OPTIONAL,
    IN PSCE_NAME_LIST pDeleteList OPTIONAL
    )
{

    if ( RealGroupName == NULL  ) {
        return(ERROR_INVALID_PARAMETER);
    }

    if ( pAddList == NULL && pDeleteList == NULL ) {

        //
        // nothing to do
        //

        return(ERROR_SUCCESS);
    }

    PLDAP    pSrhLDAP = NULL;

    SCESTATUS rc = ScepLdapOpen(&pSrhLDAP);

    if ( rc != SCESTATUS_SUCCESS ) {
        return(ScepSceStatusToDosError(rc));
    }

    PLDAPMod  rgMods[2];
    LDAPMod   Mod;
    DWORD     retErr=NO_ERROR;
    DWORD     retSave=NO_ERROR;
    PWSTR     rgpszVals[2];

    PSCE_NAME_LIST pName;

    rgMods[0] = &Mod;
    rgMods[1] = NULL;

    rgpszVals[1] = NULL;

    //
    // need to do one at a time because individual members/memberof may fail
    //

    Mod.mod_op      = LDAP_MOD_ADD;
    Mod.mod_values  = rgpszVals;

    if ( Flag == SCEGRP_MEMBERS )
        Mod.mod_type    = L"member";
    else
        Mod.mod_type    = L"memberOf";

    for ( pName=pAddList; pName != NULL; pName = pName->Next ) {

        ScepLogOutput3(2,0, SCEDLL_SCP_ADD, pName->Name);
        rgpszVals[0]  = pName->Name;

        //
        // Now, we'll do the write for members...
        //
        retErr = ldap_modify_s(pSrhLDAP,
                               RealGroupName,
                               rgMods
                               );
        retErr = LdapMapErrorToWin32(retErr);

        //
        // if the same member already exist, do not consider it as an error
        //

        if ( retErr == ERROR_ALREADY_EXISTS )
            retErr = ERROR_SUCCESS;

        if ( retErr != ERROR_SUCCESS ) {

            ScepLogOutput3(1,retErr, SCEDLL_SCP_ERROR_ADDTO, RealGroupName);
            retSave = retErr;
        }
    }

    if ( Flag == SCEGRP_MEMBERS && pDeleteList ) {

        //
        // remove existing members. Note, memberof won't be removed
        //

        if ( NO_ERROR == retSave ) {

            //
            // only remove existing members if all members are added successfully
            //

            Mod.mod_op      = LDAP_MOD_DELETE;
            Mod.mod_type    = L"member";
            Mod.mod_values  = rgpszVals;

            for ( pName=pDeleteList; pName != NULL; pName = pName->Next ) {

                ScepLogOutput3(2,0, SCEDLL_SCP_REMOVE, pName->Name);

                rgpszVals[0]  = pName->Name;

                //
                // Now, we'll do the write for members...
                //
                retErr = ldap_modify_s(pSrhLDAP,
                                       RealGroupName,
                                       rgMods
                                       );
                retErr = LdapMapErrorToWin32(retErr);

                //
                // if the member doesn't exist in the group, ignore
                //

                if ( retErr == ERROR_FILE_NOT_FOUND ) {
                    retErr = ERROR_SUCCESS;
                }

                if ( retErr != ERROR_SUCCESS) {

                    ScepLogOutput3(1,retErr, SCEDLL_SCP_ERROR_REMOVE, RealGroupName);
                    retSave = retErr;
                }
            }

        } else {

            //
            // something is wrong when adding new members
            // so existing members won't be removed
            //

            ScepLogOutput3(1,retSave, SCEDLL_SCP_ERROR_NOREMOVE);
        }
    }

    if ( pSrhLDAP ) {
        ScepLdapClose(&pSrhLDAP);
        pSrhLDAP = NULL;
    }

    return(retSave);
}
#endif

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Functions to analyze group membership in DS
//
//
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

SCESTATUS
ScepAnalyzeDsGroups(
    IN PSCE_GROUP_MEMBERSHIP pGroupMembership
    )
/* ++

Routine Description:

   Analyze the ds group membership. The main difference of ds groups
   from NT4 groups is that now group can be a member of another group.
   Members in the group are configured exactly as the pMembers list in
   the restricted group. The group is only validated (added) as a member
   of the MemberOf group list. Other existing members in those groups
   won't be removed.

   The restricted groups are specified in the SCP profile by group name.
   It could be a global group, or a alias (no difference in NT5 DS),
   but must be defined in the local domain.

Arguments:

    pGroupMembership - The restricted group list with members/memberof info to configure

Return value:

   SCESTATUS error codes

++ */
{

#if _WIN32_WINNT<0x0500

    return(SCESTATUS_SUCCESS);
#else

    if ( pGroupMembership == NULL ) {

        ScepPostProgress(TICKS_GROUPS,
                         AREA_GROUP_MEMBERSHIP,
                         NULL);

        return(SCESTATUS_SUCCESS);
    }

    SCESTATUS           rc;
    DWORD               nGroupCount=0;
    PSCE_GROUP_MEMBERSHIP pGroup=pGroupMembership;
    PWSTR               KeyName=NULL;
    DWORD GroupLen;

    //
    // open local policy
    //
    LSA_HANDLE PolicyHandle=NULL;

    rc = RtlNtStatusToDosError(
             ScepOpenLsaPolicy(
                   POLICY_LOOKUP_NAMES,
                   &PolicyHandle,
                   TRUE
                   ));
    if (ERROR_SUCCESS != rc ) {
        ScepLogOutput3(1, rc, SCEDLL_LSA_POLICY);
        return(ScepDosErrorToSceStatus(rc));
    }

    //
    // open the Ldap server, should open two ldap server, one for the local domain only
    // the other is for the global search (for members, membership)
    //
    rc = ScepLdapOpen(&pGrpLDAP);

    if ( rc == SCESTATUS_SUCCESS ) {
        ScepCrackOpen(&hDS);
    }

    if ( rc == SCESTATUS_SUCCESS ) {

        //
        // get the root of the domain
        //
        PSCE_OBJECT_LIST pRoots=NULL;

        rc = ScepEnumerateDsObjectRoots(
                    pGrpLDAP,
                    &pRoots
                    );

        if ( rc == SCESTATUS_SUCCESS ) {

            //
            // configure each group
            //
            DWORD               Win32rc;
            DWORD               rc32=NO_ERROR;   // saved status
            BOOL                bAdminFound=FALSE;

            //
            // get the local administratos group name
            //

            for ( pGroup=pGroupMembership; pGroup != NULL; pGroup=pGroup->Next ) {

                if ( KeyName ) {
                    LocalFree(KeyName);
                    KeyName = NULL;
                }

                LPTSTR pTemp = wcschr(pGroup->GroupName, L'\\');
                if ( pTemp ) {

                    //
                    // there is a domain name, check it with computer name
                    //
                    UNICODE_STRING uName;

                    uName.Buffer = pGroup->GroupName;
                    uName.Length = ((USHORT)(pTemp-pGroup->GroupName))*sizeof(TCHAR);

                    if ( !ScepIsDomainLocal(&uName) ) {
                        ScepLogOutput3(1, 0, SCEDLL_NO_MAPPINGS, pGroup->GroupName);
                        rc = SCESTATUS_INVALID_DATA;

                        ScepRaiseErrorString(
                                    NULL,
                                    KeyName ? KeyName : pGroup->GroupName,
                                    szMembers
                                    );
                        pGroup->Status |= SCE_GROUP_STATUS_DONE_IN_DS;

                        continue;
                    }

                    ScepConvertNameToSidString(
                            PolicyHandle,
                            pGroup->GroupName,
                            FALSE,
                            &KeyName,
                            &GroupLen
                            );
                    pTemp++;

                } else {
                    pTemp = pGroup->GroupName;
                    GroupLen = wcslen(pTemp);
                }

                //
                // find the group (validate) in this domain
                //
                Win32rc = ScepDsAnalyzeGroupMembers(
                                         PolicyHandle,
                                         pRoots,
                                         pTemp, // pGroup->GroupName,
                                         KeyName ? KeyName : pGroup->GroupName,
                                         GroupLen,
                                         &(pGroup->Status),
                                         pGroup->pMembers,
                                         pGroup->pMemberOf,
                                         &nGroupCount
                                         );

                if ( (Win32rc != ERROR_SUCCESS) &&
                     (pGroup->Status & SCE_GROUP_STATUS_DONE_IN_DS) ) {

                    ScepLogOutput3(1, Win32rc, SCEDLL_SAP_ERROR_ANALYZE, pGroup->GroupName);

                    rc32 = Win32rc;

                    if ( Win32rc == ERROR_FILE_NOT_FOUND ||
                         Win32rc == ERROR_SHARING_VIOLATION ||
                         Win32rc == ERROR_ACCESS_DENIED ) {

                        ScepRaiseErrorString(
                                    NULL,
                                    KeyName ? KeyName : pGroup->GroupName,
                                    szMembers
                                    );

                        Win32rc = ERROR_SUCCESS;
                    } else
                        break;
                }

            }

            if ( rc32 != NO_ERROR ) {
                rc = ScepDosErrorToSceStatus(rc32);
            }
            //
            // free pRoots
            //
            ScepFreeObjectList(pRoots);

        }

    }

    if ( KeyName ) {
        LocalFree(KeyName);
    }

    if ( pGrpLDAP ) {
        ScepLdapClose(&pGrpLDAP);
        pGrpLDAP = NULL;
    }

    ScepCrackClose(&hDS);
    hDS = NULL;

/*
    // this will be handled in the analysis into SAM
    //
    // raise groups that are errored
    //
    for ( PSCE_GROUP_MEMBERSHIP pTmpGrp=pGroup;
          pTmpGrp != NULL; pTmpGrp = pTmpGrp->Next ) {

        if ( pTmpGrp->GroupName == NULL )
            continue;

        if ( wcschr(pGroup->GroupName, L'\\') ) {

            ScepConvertNameToSidString(
                    PolicyHandle,
                    pGroup->GroupName,
                    FALSE,
                    &KeyName,
                    &GroupLen
                    );
        }

        ScepRaiseErrorString(
                 NULL,
                 KeyName ? KeyName : pTmpGrp->GroupName,
                 szMembers
                 );
        if ( KeyName ) {
            LocalFree(KeyName);
            KeyName = NULL;
        }
    }

    if ( rc != SCESTATUS_SERVICE_NOT_SUPPORT &&
         nGroupCount < TICKS_GROUPS ) {

        ScepPostProgress(TICKS_GROUPS-nGroupCount,
                     AREA_GROUP_MEMBERSHIP,
                     NULL);
    }
*/
    if ( PolicyHandle ) {
        LsaClose(PolicyHandle);
    }

    return(rc);
#endif

}


#if _WIN32_WINNT>=0x0500

DWORD
ScepDsAnalyzeGroupMembers(
    IN LSA_HANDLE LsaPolicy,
    IN PSCE_OBJECT_LIST pRoots,
    IN PWSTR GroupName,
    IN PWSTR KeyName,
    IN DWORD KeyLen,
    IN OUT DWORD *pStatus,
    IN PSCE_NAME_LIST pMembers,
    IN PSCE_NAME_LIST pMemberOf,
    IN DWORD *nGroupCount
    )
{
    if ( GroupName == NULL ) {
        return(ERROR_SUCCESS);
    }

    if ( pRoots == NULL ) {
        return(ERROR_INVALID_PARAMETER);
    }

    DWORD retErr=ERROR_SUCCESS;
    //
    // search for the name, if find it, get members and memberof
    //
    LDAPMessage *Message = NULL;
    PWSTR    Attribs[4];

    Attribs[0] = L"distinguishedName";
    Attribs[1] = L"member";
    Attribs[2] = L"memberOf";
    Attribs[3] = NULL;

    WCHAR tmpBuf[128];

//    wcscpy(tmpBuf, L"( &(|(objectClass=localGroup)(objectClass=group))(cn=");
//    wcscpy(tmpBuf, L"( &(|(objectClass=localGroup)(objectClass=group))(samAccountName=");
    wcscpy(tmpBuf, L"( &(&(|");
    swprintf(tmpBuf+wcslen(L"( &(&(|"), L"(groupType=%d)(groupType=%d))(objectClass=group))(samAccountName=\0",
             GROUP_TYPE_ACCOUNT_GROUP | GROUP_TYPE_SECURITY_ENABLED, GROUP_TYPE_UNIVERSAL_GROUP | GROUP_TYPE_SECURITY_ENABLED);

    PWSTR Filter;
    DWORD Len=wcslen(GroupName);

    Filter = (PWSTR)LocalAlloc(LMEM_ZEROINIT, (wcslen(tmpBuf)+Len+4)*sizeof(WCHAR));

    if ( Filter == NULL ) {
        return(SCESTATUS_NOT_ENOUGH_RESOURCE);
    }

    swprintf(Filter, L"%s%s) )", tmpBuf, GroupName);

    pGrpLDAP->ld_options = 0; // no chased referrel

    retErr = ldap_search_s(
              pGrpLDAP,
              pRoots->Name,
              LDAP_SCOPE_SUBTREE,
              Filter,
              Attribs,
              0,
              &Message);

    retErr = LdapMapErrorToWin32(retErr);

    if(retErr == ERROR_SUCCESS) {
        //
        // find the group
        //
        LDAPMessage *Entry = NULL;
        //
        // should only have one entry, unless there are duplicate groups
        // within the domain, in which case, we only care the first entry anyway
        //
        //
        // get the first one.
        //
        Entry = ldap_first_entry(pGrpLDAP, Message);

        if(Entry != NULL) {

            PWSTR  *Values;

            Values = ldap_get_values(pGrpLDAP, Entry, Attribs[0]);

            if(Values != NULL) {

                ScepLogOutput3(1,0, SCEDLL_SAP_ANALYZE, GroupName);

                if ( *nGroupCount < TICKS_GROUPS ) {
                    ScepPostProgress(1,
                                     AREA_GROUP_MEMBERSHIP,
                                     GroupName);
                    *nGroupCount++;
                }

                ScepLogOutput2(3,0, L"\t\t%s", Values[0]);
                ldap_value_free(Values);

                PSCE_NAME_LIST pRealNames=NULL;
                PSCE_NAME_LIST pCurrentList=NULL;
                BOOL bDifferent;
                DWORD retErr2, rc;

                //
                // translate each name in the pMembers list to real ds names (search)
                //
                retErr = ScepDsGetDsNameList(pMembers, &pRealNames);

                if ( ERROR_SUCCESS == retErr ||
                     ERROR_FILE_NOT_FOUND == retErr ) {

                    //
                    // analyze members
                    //
                    Values = ldap_get_values(pGrpLDAP, Entry, Attribs[1]);

                    rc = ScepDsMembersDifferent(SCEGRP_MEMBERS,
                                                Values,
                                                &pRealNames,
                                                &pCurrentList,
                                                &bDifferent);

                    if ( Values != NULL )
                        ldap_value_free(Values);

                    //
                    // if there are some names unresolvable, this should be
                    // treated as mismatch
                    //
                    if ( ERROR_FILE_NOT_FOUND == retErr )
                        bDifferent = TRUE;

                    retErr = rc;

                    if ( ( ERROR_SUCCESS == retErr ) &&
                         ( bDifferent ||
                           (*pStatus & SCE_GROUP_STATUS_NC_MEMBERS) ) ) {
                        //
                        // save to the database
                        //

                        retErr = ScepDsConvertDsNameList(pCurrentList);

                        if ( retErr == NO_ERROR ) {
                            retErr = ScepSaveMemberMembershipList(
                                                LsaPolicy,
                                                szMembers,
                                                KeyName,
                                                KeyLen,
                                                pCurrentList,
                                                (*pStatus & SCE_GROUP_STATUS_NC_MEMBERS) ? 2: 1);
                        }

                        if ( retErr != ERROR_SUCCESS ) {
                            ScepLogOutput3(1,retErr, SCEDLL_SAP_ERROR_SAVE, GroupName);
                        }
                    }

                    ScepFreeNameList(pCurrentList);
                    pCurrentList = NULL;

                    ScepFreeNameList(pRealNames);
                    pRealNames = NULL;
                }

                retErr2 = ScepDsGetDsNameList(pMemberOf, &pRealNames);

                if ( ( ERROR_SUCCESS == retErr2 ||
                       ERROR_FILE_NOT_FOUND == retErr2 ) && pRealNames ) {
                    //
                    // analyze membership
                    //
                    Values = ldap_get_values(pGrpLDAP, Entry, Attribs[2]);

                    rc = ScepDsMembersDifferent(SCEGRP_MEMBERSHIP,
                                                Values,
                                                &pRealNames,
                                                &pCurrentList,
                                                &bDifferent);

                    if ( Values != NULL )
                        ldap_value_free(Values);

                    //
                    // if there are some names unresolvable, this should be
                    // treated as mismatch
                    //
                    if ( ERROR_FILE_NOT_FOUND == retErr )
                        bDifferent = TRUE;

                    retErr2 = rc;

                    if ( (retErr2 == NO_ERROR) &&
                         ( bDifferent ||
                           (*pStatus & SCE_GROUP_STATUS_NC_MEMBEROF) ) ) {
                        //
                        // save to the database
                        //
                        retErr2 = ScepDsConvertDsNameList(pCurrentList);

                        if ( retErr2 == NO_ERROR ) {
                            retErr2 = ScepSaveMemberMembershipList(
                                            LsaPolicy,
                                            szMemberof,
                                            KeyName,
                                            KeyLen,
                                            pCurrentList,
                                            (*pStatus & SCE_GROUP_STATUS_NC_MEMBEROF) ? 2 : 1);
                        }

                        if ( retErr2 != ERROR_SUCCESS ) {
                            ScepLogOutput3(1,retErr2, SCEDLL_SAP_ERROR_SAVE, GroupName);
                        }
                    }
                    ScepFreeNameList(pCurrentList);
                    pCurrentList = NULL;

                    ScepFreeNameList(pRealNames);
                    pRealNames = NULL;
                }

                *pStatus |= SCE_GROUP_STATUS_DONE_IN_DS;

                //
                // remember the error
                //
                if ( retErr == NO_ERROR ) {
                    retErr = retErr2;
                }

            } else {
                //
                // Value[0] (group name) may not be empty
                //
                retErr = LdapMapErrorToWin32(pGrpLDAP->ld_errno);
                ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
            }

        } else {

            retErr = ERROR_FILE_NOT_FOUND;  // the group is not found
            ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
        }
    } else {

        ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, Filter);
    }

    if ( Message )
        ldap_msgfree(Message);

    //
    // free Filter
    //
    LocalFree(Filter);

    return(retErr);
}


DWORD
ScepDsMembersDifferent(
    IN ULONG Flag,
    IN PWSTR *Values,
    IN OUT PSCE_NAME_LIST *pNameList,
    OUT PSCE_NAME_LIST *pCurrentList,
    OUT PBOOL pbDifferent
    )
{
    if ( pCurrentList == NULL || pbDifferent == NULL ) {
        return(ERROR_INVALID_PARAMETER);
    }

    if ( Values == NULL ) {

        if ( pNameList == NULL || *pNameList == NULL )
            *pbDifferent = FALSE;
        else
            *pbDifferent = TRUE;

        return(ERROR_SUCCESS);
    }

    ULONG ValCount = ldap_count_values(Values);

    DWORD rc=NO_ERROR;
    *pbDifferent = FALSE;

    for(ULONG index = 0; index < ValCount; index++) {

        if ( Values[index] == NULL ) {
            continue;
        }

        if ( !(*pbDifferent) ) {

            PSCE_NAME_LIST pTemp = *pNameList, pTemp2;
            PSCE_NAME_LIST pParent = NULL;
            INT i;

            while ( pTemp != NULL ) {

                if ( (i = _wcsicmp(Values[index], pTemp->Name)) == 0 ) {
                    //
                    // find this member
                    //
                    if ( pParent == NULL ) {
                        *pNameList = pTemp->Next;
                    } else
                        pParent->Next = pTemp->Next;

                    pTemp2 = pTemp;
                    pTemp = pTemp->Next;

                    pTemp2->Next = NULL;
                    ScepFreeNameList(pTemp2);
                    break;

                } else {
                    pParent = pTemp;
                    pTemp = pTemp->Next;
                }
            }

            if ( pTemp == NULL && i != 0 )
                *pbDifferent = TRUE;

        }
        //
        // build the current list
        //
        rc = ScepAddToNameList(pCurrentList, Values[index], 0);

        if ( rc != NO_ERROR ) {

            ScepLogOutput3(1,rc, SCEDLL_SCP_ERROR_ADD, Values[index]);
            break;
        }
    }

    if ( rc == NO_ERROR && Flag == SCEGRP_MEMBERS &&
         *pbDifferent == FALSE ) {
        //
        // still same so far, only continue to compare for members
        // because membership is not one to one configuring
        //
        if ( *pNameList != NULL )
            *pbDifferent = TRUE;

    } // pCurrentList will be freed outside

    return(rc);

}


PWSTR
ScepGetLocalAdminsName()
{

    NTSTATUS NtStatus;
    SAM_HANDLE          AccountDomain=NULL;
    SAM_HANDLE          AliasHandle=NULL;
    SAM_HANDLE          ServerHandle=NULL;
    PSID                DomainSid=NULL;

    SAM_HANDLE          theBuiltinHandle=NULL;
    PSID                theBuiltinSid=NULL;

    ALIAS_NAME_INFORMATION *BufName=NULL;
    PWSTR pAdminsName=NULL;

    //
    // open the sam account domain
    //

    NtStatus = ScepOpenSamDomain(
                    SAM_SERVER_ALL_ACCESS,
                    MAXIMUM_ALLOWED,
                    &ServerHandle,
                    &AccountDomain,
                    &DomainSid,
                    &theBuiltinHandle,
                    &theBuiltinSid
                    );

    if ( !NT_SUCCESS(NtStatus) ) {
        ScepLogOutput3(1, RtlNtStatusToDosError(NtStatus),
                       SCEDLL_ERROR_OPEN, L"SAM");
        return(NULL);
    }


    NtStatus = SamOpenAlias(
                  theBuiltinHandle,
                  MAXIMUM_ALLOWED,
                  DOMAIN_ALIAS_RID_ADMINS,
                  &AliasHandle
                  );

    if ( NT_SUCCESS( NtStatus ) ) {

        NtStatus = SamQueryInformationAlias(
                      AliasHandle,
                      AliasNameInformation,
                      (PVOID *)&BufName
                      );

        if ( NT_SUCCESS( NtStatus ) && BufName &&
             BufName->Name.Length > 0 && BufName->Name.Buffer ) {

            //
            // allocate buffer to return
            //
            pAdminsName = (PWSTR)ScepAlloc(0, BufName->Name.Length+2);

            if ( pAdminsName ) {

                wcsncpy(pAdminsName, BufName->Name.Buffer,
                        BufName->Name.Length/2);
                pAdminsName[BufName->Name.Length/2] = L'\0';

            } else {
                NtStatus = STATUS_NO_MEMORY;
            }

        }
        if ( BufName ) {

            SamFreeMemory(BufName);
            BufName = NULL;
        }

        //
        // close the user handle
        //
        SamCloseHandle(AliasHandle);

    }

    SamCloseHandle(AccountDomain);

    SamCloseHandle( ServerHandle );
    if ( DomainSid != NULL )
        SamFreeMemory(DomainSid);

    SamCloseHandle( theBuiltinHandle );
    if ( theBuiltinSid != NULL )
        SamFreeMemory(theBuiltinSid);

    return pAdminsName;
}

DWORD
ScepDsConvertDsNameList(
    IN OUT PSCE_NAME_LIST pDsNameList
    )
/*
Routine:

    The input list is in the LDAP format (CN=<>,...DC=<>, ...). When the routine
    returns, the list will be in NT4 account name format (domain\account)
*/
{
    if ( pDsNameList == NULL ) {
        return(ERROR_SUCCESS);
    }

    PFNDSCRACKNAMES pfnDsCrackNames=NULL;
    PFNDSFREENAMERESULT pfnDsFreeNameResult=NULL;

    if ( hNtdsApi ) {

#if defined(UNICODE)
        pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesW");
        pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultW");
#else
        pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesA");
        pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultA");
#endif
    }

    if ( pfnDsCrackNames == NULL || pfnDsFreeNameResult == NULL ) {
        return(ERROR_PROC_NOT_FOUND);
    }

    DWORD retErr=ERROR_SUCCESS;
    PWSTR pTemp;
    DS_NAME_RESULT *pDsResult=NULL;

    DWORD DomLen;
    DWORD SidLen;
    CHAR SidBuf[MAX_PATH];
    PWSTR RefDom[MAX_PATH];
    SID_NAME_USE SidUse;

    for ( PSCE_NAME_LIST pName = pDsNameList; pName != NULL; pName = pName->Next ) {

        if ( pName->Name == NULL ) {
            continue;
        }

        retErr = (*pfnDsCrackNames) (
                        hDS,                // in
                        DS_NAME_NO_FLAGS,   // in
                        DS_FQDN_1779_NAME,  // in
                        DS_NT4_ACCOUNT_NAME,// in
                        1,                  // in
                        &(pName->Name),     // in
                        &pDsResult);        // out


        if(retErr == ERROR_SUCCESS && pDsResult && pDsResult->rItems &&
            pDsResult->rItems[0].pName ) {

            //
            // NT4 account name format is returned, should check if the
            // domain is not a acccount domain
            //
            pTemp = wcschr(pDsResult->rItems[0].pName, L'\\');

            if ( pTemp ) {

                DomLen=MAX_PATH;
                SidLen=MAX_PATH;

                if ( LookupAccountName(
                        NULL,
                        pDsResult->rItems[0].pName,
                        (PSID)SidBuf,
                        &SidLen,
                        (PWSTR)RefDom,
                        &DomLen,
                        &SidUse
                        ) ) {

                    if ( !ScepIsSidFromAccountDomain( (PSID)SidBuf) ) {
                        //
                        // add name only
                        //
                        pTemp++;
                    } else {
                        pTemp = pDsResult->rItems[0].pName;
                    }
                } else {
                    pTemp = pDsResult->rItems[0].pName;
                }

            } else {
                pTemp = pDsResult->rItems[0].pName;
            }

            PWSTR pNewName = (PWSTR)ScepAlloc(0, (wcslen(pTemp)+1)*sizeof(WCHAR));

            if ( pNewName ) {

                wcscpy(pNewName, pTemp);
                ScepFree(pName->Name);
                pName->Name = pNewName;

            } else {
                retErr = ERROR_NOT_ENOUGH_MEMORY;
            }

        } else {
            // no match is found
            retErr = ERROR_FILE_NOT_FOUND;
            ScepLogOutput3(1,retErr, SCEDLL_CANNOT_FIND, pName->Name);

        }

        if ( pDsResult ) {
            (*pfnDsFreeNameResult) (pDsResult);
            pDsResult = NULL;
        }

        if ( retErr != ERROR_SUCCESS ) {
            break;
        }
    }

    return(retErr);
}

#endif