//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1996 - 1996.
//
//  File:       DSOBJECT.CXX
//
//  Contents:   DSObject support functions
//
//  History:    01-Jul-96        MacM           Created
//
//----------------------------------------------------------------------------
#include <aclpch.hxx>
#pragma hdrstop

#define NO_PROPAGATE

#define ACTRL_SD_PROP_NAME  L"nTSecurityDescriptor"
#define ACTRL_EXT_RIGHTS_CONTAINER L"CN=Extended-Rights,"

#include <dsgetdc.h>
#include <lmapibuf.h>
#include <mapicode.h>
extern "C"
{
    #include <permit.h>
    #include <seopaque.h>
    #include <sertlp.h>
    #include <ntdsguid.h>
    #include <ntldap.h>
}

#define PSD_FROM_DS_PSD(psd)  (PSECURITY_DESCRIPTOR)((PBYTE)psd + sizeof(ULONG))

#define BYTE_0_MASK 0xFF
#define BYTE_3(Value) (UCHAR)(  (Value)        & BYTE_0_MASK)
#define BYTE_2(Value) (UCHAR)( ((Value) >>  8) & BYTE_0_MASK)
#define BYTE_1(Value) (UCHAR)( ((Value) >> 16) & BYTE_0_MASK)
#define BYTE_0(Value) (UCHAR)( ((Value) >> 24) & BYTE_0_MASK)

#define MartaPutUlong(Buffer, Value) {          \
    ((PBYTE)Buffer)[0] = BYTE_0(Value),         \
    ((PBYTE)Buffer)[1] = BYTE_1(Value),         \
    ((PBYTE)Buffer)[2] = BYTE_2(Value),         \
    ((PBYTE)Buffer)[3] = BYTE_3(Value);         \
}

DWORD
ConvertStringAToStringW (
    IN  PSTR            pszString,
    OUT PWSTR          *ppwszString
)
/*++

Routine Description:

    This routine will convert an ASCII string to a UNICODE string.

    The returned string buffer must be freed via a call to LocalFree


Arguments:

    pszString - The string to convert
    ppwszString - Where the converted string is returned

Return Value:

    ERROR_SUCCESS - Success
    ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed

--*/
{

    if(pszString == NULL)
    {
        *ppwszString = NULL;
    }
    else
    {
        ULONG cLen = strlen(pszString);
        *ppwszString = (PWSTR)AccAlloc(sizeof(WCHAR) *
                                                        (mbstowcs(NULL, pszString, cLen + 1) + 1));
        if(*ppwszString  != NULL)
        {
             mbstowcs(*ppwszString,
                      pszString,
                      cLen + 1);
        }
        else
        {
            return(ERROR_NOT_ENOUGH_MEMORY);
        }
    }

    return(ERROR_SUCCESS);
}




DWORD
ConvertStringWToStringA (
    IN  PWSTR           pwszString,
    OUT PSTR           *ppszString
)
/*++

Routine Description:

    This routine will convert a UNICODE string to an ANSI string.

    The returned string buffer must be freed via a call to LocalFree


Arguments:

    pwszString - The string to convert
    ppszString - Where the converted string is returned



Return Value:

    ERROR_SUCCESS - Success
    ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed

--*/
{

    if(pwszString == NULL)
    {
        *ppszString = NULL;
    }
    else
    {
        ULONG cLen = wcslen(pwszString);
        *ppszString = (PSTR)AccAlloc(sizeof(CHAR) *
                                  (wcstombs(NULL, pwszString, cLen + 1) + 1));
        if(*ppszString  != NULL)
        {
             wcstombs(*ppszString,
                      pwszString,
                      cLen + 1);
        }
        else
        {
            return(ERROR_NOT_ENOUGH_MEMORY);
        }
    }

    return(ERROR_SUCCESS);
}

//+---------------------------------------------------------------------------
//
//  Function:   DspSplitPath
//
//  Synopsis:   This function splits a path into the server portion and the
//              path portion.  If the server portion doesn't exist, a NULL is
//              returned
//
//  Arguments:  [IN  pwszObjectPath]--  The name of the object to be split
//              [OUT ppwszAllocatedServer]  -- Where the server name is returned.
//                                      Must be freed via AccFree
//              [OUT ppwszReferencePath]    --  Ptr within the input path that
//                                      contains the path portion.
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//              ERROR_PATH_NOT_FOUND--  The object was not reachable
//              ERROR_NOT_ENOUGH_MEMORY A memory allocation failed
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD DspSplitPath(IN  PWSTR    pwszObjectPath,
                   OUT PWSTR   *ppwszAllocatedServer,
                   OUT PWSTR   *ppwszReferencePath)
{
    DWORD dwErr = ERROR_SUCCESS;
    PWSTR Temp = NULL;
    ULONG Len = 0;

    if(IS_UNC_PATH(pwszObjectPath, wcslen(pwszObjectPath)))
    {

        Temp = wcschr(pwszObjectPath + 2, L'\\');

        if (Temp == NULL) {

            Len = wcslen(pwszObjectPath);
        }
        else
        {
            Len = (ULONG)(Temp - pwszObjectPath);
        }

        *ppwszAllocatedServer = ( PWSTR )AccAlloc( ( Len + 1 ) * sizeof( WCHAR ) );

        if(*ppwszAllocatedServer == NULL)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;

        }
        else
        {
            wcsncpy( *ppwszAllocatedServer, pwszObjectPath, Len );
            *( *ppwszAllocatedServer + Len ) = UNICODE_NULL;
        }

        if(Temp != NULL)
        {
            *ppwszReferencePath = Temp + 1;
        }
        else
        {
            *ppwszReferencePath = NULL;
        }
    }
    else
    {
        *ppwszReferencePath = pwszObjectPath;
        *ppwszAllocatedServer = NULL;
    }

    return(dwErr);
}

//+---------------------------------------------------------------------------
//
//  Function:   PingDSObjByNameRes
//
//  Synopsis:   "Pings" the specified DS object, to determine if it is
//              reachable or not
//
// REMOVE POST BETA - 1.  Raid 107329
//
//  Arguments:  [IN  pObjectName]   --  The name of the object
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//              ERROR_PATH_NOT_FOUND--  The object was not reachable
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD
PingDSObjByNameRes(IN PWSTR pwszDSObj,
                   IN PDS_NAME_RESULTW pNameRes)
{
    acDebugOut((DEB_TRACE, "in PingDSObjByNameRes\n"));
    DWORD dwErr;

    if(pNameRes->cItems == 0 || pNameRes->rItems[0].status != 0)
    {
        dwErr = ERROR_PATH_NOT_FOUND;
    }
    else
    {
        //
        // Now, we'll bind to the object, and then do the read
        //
        PLDAP   pLDAP;

        dwErr = BindToDSObject(NULL,
                               pNameRes->rItems[0].pDomain,
                               &pLDAP);

        if(dwErr == ERROR_SUCCESS)
        {
            PLDAPMessage    pMessage = NULL;
            PWSTR           rgAttribs[2];

            rgAttribs[0] = L"distinguishedName";
            rgAttribs[1] = NULL;


            if(dwErr == ERROR_SUCCESS)
            {
                dwErr = ldap_search_s(pLDAP,
                                      (PWSTR)pwszDSObj,
                                      LDAP_SCOPE_BASE,
                                      L"(objectClass=*)",
                                      rgAttribs,
                                      0,
                                      &pMessage);

                dwErr = LdapMapErrorToWin32( dwErr );
            }

            if(dwErr == ERROR_SUCCESS)
            {
                LDAPMessage *pEntry = NULL;
                pEntry = ldap_first_entry(pLDAP,
                                          pMessage);

                if(pEntry == NULL)
                {
                    dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                }
                else
                {
                    //
                    // Now, we'll have to get the values
                    //
                    PWSTR *ppwszValues = ldap_get_values(pLDAP,
                                                         pEntry,
                                                         rgAttribs[0]);
                    if(ppwszValues == NULL)
                    {

                        if(pLDAP->ld_errno == LDAP_NO_SUCH_ATTRIBUTE )
                        {

                            dwErr =  ERROR_SUCCESS;
                        }
                        else
                        {
                            dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                        }
                    }
                    else
                    {
                        ldap_value_free(ppwszValues);
                    }
                }

                ldap_msgfree(pMessage);
            }
        }
    }

    acDebugOut((DEB_TRACE, "out PingDSObjByNameRes: %lu\n", dwErr));
    return(dwErr);
}

//+---------------------------------------------------------------------------
//
//  Function:   DspBindAndCrack
//
//  Synopsis:   Does a DsCrackName on the object
//
//  Arguments:  [IN  pwszServer]    --  Optional server name to bind to
//              [IN  pwszDSObj]     --  The DS object to bind to
//              [OUT pResults]      --  The returned cracked name
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD DspBindAndCrack( IN  PWSTR pwszServer, OPTIONAL
                       IN  PWSTR pwszDSObj,
                       IN  DWORD OptionalDsGetDcFlags,
                       OUT PDS_NAME_RESULTW *pResults )
{
    return DspBindAndCrackEx( pwszServer,
                              pwszDSObj,
                              OptionalDsGetDcFlags,
                              DS_FQDN_1779_NAME,
                              pResults );
}


//+---------------------------------------------------------------------------
//
//  Function:   DspBindAndCrackEx
//
//  Synopsis:   Does a DsCrackName on the object
//
//  Arguments:  [IN  pwszServer]    --  Optional server name to bind to
//              [IN  pwszDSObj]     --  The DS object to bind to
//              [IN  formatDesired] --  indicates the format of the returned name
//              [OUT pResults]      --  The returned cracked name
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD DspBindAndCrackEx( IN  PWSTR pwszServer,
                         IN  PWSTR pwszDSObj,
                         IN  DWORD OptionalDsGetDcFlags,
                         IN  DS_NAME_FORMAT formatDesired,
                         OUT PDS_NAME_RESULTW *pResults )
{
    DWORD dwErr = ERROR_SUCCESS;

    HANDLE  hDS = NULL;
    PDS_NAME_RESULTW   pNameRes;
    PDOMAIN_CONTROLLER_INFOW pDCI = NULL;
    BOOL NamedServer = FALSE;

    //
    // The path we are given could be of the form \\\\servername\\path.  If it is, it
    // is not necessary to do the DsGetDcName call.  We'll just use the server name
    // we are given
    //
    if(pwszServer != NULL)
    {
        NamedServer = TRUE;
    }
    else
    {
        dwErr = DsGetDcNameW(NULL,
                             NULL,
                             NULL,
                             NULL,
                             DS_DIRECTORY_SERVICE_REQUIRED | OptionalDsGetDcFlags,  // DS_IP_REQUIRED
                             &pDCI);

        if(dwErr == ERROR_SUCCESS)
        {
            pwszServer = pDCI[0].DomainControllerName; // pDCI[0].DomainControllerAddress;
        }
    }


    //
    // Do the bind and crack
    //
    if(dwErr == ERROR_SUCCESS)
    {
        dwErr = DsBindW(pwszServer,
                        NULL,
                        &hDS);

        if(dwErr == ERROR_SUCCESS)
        {
            dwErr = DsCrackNamesW(hDS,
                                  DS_NAME_NO_FLAGS,
                                  DS_UNKNOWN_NAME,
                                  formatDesired,
                                  1,
                                  &pwszDSObj,
                                  &pNameRes);

            if (dwErr == ERROR_SUCCESS)
            {

                if(pNameRes->cItems != 0 &&
                   pNameRes->rItems[0].status == DS_NAME_ERROR_DOMAIN_ONLY &&
                   NamedServer == FALSE )
                {
                    NetApiBufferFree(pDCI);
                    pDCI = NULL;
                    dwErr = DsGetDcNameW(NULL,
                                         pNameRes->rItems[0].pDomain,
                                         NULL,
                                         NULL,
                                         DS_DIRECTORY_SERVICE_REQUIRED | OptionalDsGetDcFlags, // DS_IP_REQUIRED |
                                         &pDCI);
                    if(dwErr == ERROR_SUCCESS)
                    {
                        DsUnBindW(&hDS);
                        hDS = NULL;

                        dwErr = DsBindW(pDCI[0].DomainControllerName, // DomainControllerAddress,
                                        NULL,
                                        &hDS);

                        if(dwErr == ERROR_SUCCESS)
                        {
                            dwErr = DsCrackNamesW(hDS,
                                                  DS_NAME_NO_FLAGS,
                                                  DS_UNKNOWN_NAME,
                                                  formatDesired,
                                                  1,
                                                  &pwszDSObj,
                                                  &pNameRes);
                        }

                    }

                }

                //
                // If this is a case where we don't have a named server, handle
                // the case where an object was created on one Dc, but we've just
                // bound to a second one
                //
                //
                if (dwErr == ERROR_SUCCESS && formatDesired == DS_FQDN_1779_NAME &&
                    NamedServer == FALSE )
                {
                    dwErr = PingDSObjByNameRes( pwszDSObj,pNameRes );

                    if(dwErr != ERROR_SUCCESS)
                    {
                        DsFreeNameResultW(pNameRes);
                    }

                    if(dwErr == ERROR_PATH_NOT_FOUND && OptionalDsGetDcFlags == 0)
                    {
                        dwErr = DspBindAndCrackEx( pDCI[0].DomainControllerName, //DomainControllerAddress,
                                                   pwszDSObj,
                                                   DS_WRITABLE_REQUIRED,
                                                   formatDesired,
                                                   &pNameRes );
                    }
                }

                *pResults = pNameRes;
            }


            if(hDS != NULL)
            {
                DsUnBindW(&hDS);
            }
        }
    }

    if(pDCI != NULL)
    {
        NetApiBufferFree(pDCI);
    }

    return( dwErr );
}


//+---------------------------------------------------------------------------
//
//  Function:   BindToDSObject
//
//  Synopsis:   Binds to a DS object
//
//  Arguments:  [IN  pwszServer]    --  OPTIONAL.  If specified, this is the name
//                                      of the server to bind to
//              [IN  pwszDSObj]     --  The DS object to bind to
//              [OUT ppLDAP]        --  The returned LDAP handle
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//              ERROR_PATH_NOT_FOUND--  The object was not reachable
//
//  Notes:      The returned LDAP handle must be closed via UnbindFromDSObject
//
//----------------------------------------------------------------------------
DWORD   BindToDSObject(IN  PWSTR    pwszServer, OPTIONAL
                       IN  LPWSTR   pwszDSObj,
                       OUT PLDAP   *ppLDAP)
{
    acDebugOut((DEB_TRACE, "in BindToDSObject\n"));
    PDOMAIN_CONTROLLER_INFOW pDCI = NULL;
    DWORD dwErr = ERROR_SUCCESS;

    //
    // The path we are given could be of the form \\\\servername\\path.  If it is, it
    // is not necessary to do the DsGetDcName call.  We'll just use the server name
    // we are given
    //
    // Change: in order to use mutual authentication, A DNS format domain name must
    // be passed into ldap_open/ldap_init. So even a servername is passed in, it's
    // necessary to call DsGetDcNameW to get the DNS format domain name.
    // Since we asked for DIRECTORY_SERVICE_REQUIRED, this call won't talk to any
    // NT4 domain and the DNS name should always be returned. If it fails to get the
    // DNS name, we will fail this function - by design.
    //

    dwErr = DsGetDcNameW(pwszServer,
                         NULL,
                         NULL,
                         NULL,
                         DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME,
                         &pDCI);


    if(dwErr == ERROR_SUCCESS)
    {
        *ppLDAP = ldap_open(pDCI->DomainName, LDAP_PORT);

        if(*ppLDAP == NULL)
        {
            dwErr = ERROR_PATH_NOT_FOUND;
        }
        else
        {
            //
            // Do a bind...
            //
            dwErr = ldap_bind_s(*ppLDAP,
                                NULL,
                                NULL,
                                LDAP_AUTH_SSPI);

        }

    }

    if(pDCI != NULL)
    {
        NetApiBufferFree(pDCI);
    }

    acDebugOut((DEB_TRACE, "out BindToDSObject: %lu\n", dwErr));
    return(dwErr);
}




//+---------------------------------------------------------------------------
//
//  Function:   UnBindFromDSObject
//
//  Synopsis:   Closes a binding to a DS object
//
//  Arguments:  [IN  ppLDAP]        --  The LDAP connection to close
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD   UnBindFromDSObject(OUT PLDAP               *ppLDAP)
{
    acDebugOut((DEB_TRACE, "in UnBindFromDSObject\n"));
    DWORD dwErr = ERROR_SUCCESS;

    if(*ppLDAP != NULL)
    {
        ldap_unbind(*ppLDAP);
        *ppLDAP = NULL;
    }

    acDebugOut((DEB_TRACE, "out UnBindFromDSObject: %lu\n", dwErr));
    return(dwErr);
}




//+---------------------------------------------------------------------------
//
//  Function:   ReadDSObjSecDesc
//
//  Synopsis:   Reads the security descriptor from the specied object via
//              the open ldap connection
//
//  Arguments:  [IN  pLDAP]         --  The open LDAP connection
//              [IN  SeInfo]        --  Parts of the security descriptor to
//                                      read.
//              [IN  pwszDSObj]     --  The DSObject to get the security
//                                      descriptor for
//              [OUT ppSD]          --  Where the security descriptor is
//                                      returned
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//              ERROR_NOT_ENOUGH_MEMORY A memory allocation failed
//
//  Notes:      The returned security descriptor must be freed with LocalFree
//
//----------------------------------------------------------------------------
DWORD
ReadDSObjSecDesc(IN  PLDAP                  pLDAP,
                 IN  PWSTR                  pwszObject,
                 IN  SECURITY_INFORMATION   SeInfo,
                 OUT PSECURITY_DESCRIPTOR  *ppSD)
{
    DWORD   dwErr = ERROR_SUCCESS;

    PLDAPMessage    pMessage = NULL;
    PWSTR           rgAttribs[2];
    BYTE            berValue[8];

    //
    // JohnsonA The BER encoding is current hardcoded.  Change this to use
    // AndyHe's BER_printf package once it's done.
    //

    berValue[0] = 0x30;
    berValue[1] = 0x03;
    berValue[2] = 0x02;
    berValue[3] = 0x01;
    berValue[4] = (BYTE)((ULONG)SeInfo & 0xF);

    LDAPControl     SeInfoControl =
                    {
                        LDAP_SERVER_SD_FLAGS_OID_W,
                        {
                            5, (PCHAR)berValue
                        },
                        TRUE
                    };

    PLDAPControl    ServerControls[2] =
                    {
                        &SeInfoControl,
                        NULL
                    };

    rgAttribs[0] = ACTRL_SD_PROP_NAME;
    rgAttribs[1] = NULL;



    if(dwErr == ERROR_SUCCESS)
    {
        dwErr = ldap_search_ext_s(pLDAP,
                                  pwszObject,
                                  LDAP_SCOPE_BASE,
                                  L"(objectClass=*)",
                                  rgAttribs,
                                  0,
                                  (PLDAPControl *)&ServerControls,
                                  NULL,
                                  NULL,
                                  10000,
                                  &pMessage);

        dwErr = LdapMapErrorToWin32( dwErr );
    }

    if(dwErr == ERROR_SUCCESS)
    {
        LDAPMessage *pEntry = NULL;
        pEntry = ldap_first_entry(pLDAP,
                                  pMessage);

        if(pEntry == NULL)
        {
            dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
        }
        else
        {
            //
            // Now, we'll have to get the values
            //
            PWSTR *ppwszValues = ldap_get_values(pLDAP,
                                                 pEntry,
                                                 rgAttribs[0]);
            if(ppwszValues == NULL)
            {
                if(pLDAP->ld_errno == LDAP_NO_SUCH_ATTRIBUTE)
                {
                    dwErr = ERROR_ACCESS_DENIED;
                }
                else
                {
                    dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                }

            }
            else
            {
                PLDAP_BERVAL *pSize = ldap_get_values_len(pLDAP,
                                                          pMessage,
                                                          rgAttribs[0]);
                if(pSize == NULL)
                {
                    dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                }
                else
                {
                    //
                    // Allocate the security descriptor to return
                    //
                    *ppSD = (PSECURITY_DESCRIPTOR)AccAlloc((*pSize)->bv_len);
                    if(*ppSD == NULL)
                    {
                        dwErr = ERROR_NOT_ENOUGH_MEMORY;
                    }
                    else
                    {
                        memcpy(*ppSD,
                               (PBYTE)(*pSize)->bv_val,
                               (*pSize)->bv_len);
                    }
                    ldap_value_free_len(pSize);
                }

                ldap_value_free(ppwszValues);
            }
        }

        ldap_msgfree(pMessage);
    }

    return(dwErr);
}




//+---------------------------------------------------------------------------
//
//  Function:   GetSDForDSObj
//
//  Synopsis:   Gets a security descriptor from a DS object
//
//  Arguments:  [IN  pwszDSObj]     --  The DSObject to get the security
//                                      descriptor for
//              [OUT ppSD]          --  Where the security descriptor is
//                                      returned
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//              ERROR_NOT_ENOUGH_MEMORY A memory allocation failed
//              ERROR_INVALID_PARAMETER The object name that was given was in
//                                      a bad format (not \\x\y)
//
//  Notes:      The returned security descriptor must be freed with LocalFree
//
//----------------------------------------------------------------------------
DWORD
GetSDForDSObj(IN  LPWSTR                pwszDSObj,
              IN  SECURITY_INFORMATION  SeInfo,
              OUT PSECURITY_DESCRIPTOR *ppSD)
{
    acDebugOut((DEB_TRACE, "in GetSDForDSObj\n"));
    DWORD dwErr = ERROR_SUCCESS;
    PWSTR pwszServer = NULL, pwszPath = NULL;

    dwErr = DspSplitPath(pwszDSObj,
                         &pwszServer,
                         &pwszPath);

    if(dwErr == ERROR_SUCCESS)
    {

        //
        // Convert the name into attributed format
        //
        PDS_NAME_RESULTW   pNameRes;

        dwErr = DspBindAndCrack( pwszServer, pwszPath, 0, &pNameRes );

        if(dwErr == ERROR_SUCCESS)
        {
            if(pNameRes->cItems == 0 || pNameRes->rItems[0].status != 0)
            {
                dwErr = ERROR_PATH_NOT_FOUND;
            }
            else
            {
                //
                // Now, we'll bind to the object, and then do the read
                //
                PLDAP   pLDAP;

                dwErr = BindToDSObject(pwszServer,
                                       pNameRes->rItems[0].pDomain,
                                       &pLDAP);

                if(dwErr == ERROR_SUCCESS)
                {
                    //
                    // Now, we'll do the read...
                    //
                    dwErr = ReadDSObjSecDesc(pLDAP,
                                             pNameRes->rItems[0].pName,
                                             SeInfo,
                                             ppSD);
                    UnBindFromDSObject(&pLDAP);
                }
            }
            DsFreeNameResultW(pNameRes);
        }

        AccFree(pwszServer);
    }

    acDebugOut((DEB_TRACE, "Out GetSDForDSObj: %lu\n", dwErr));
    return(dwErr);
}




//+---------------------------------------------------------------------------
//
//  Function:   ReadDSObjPropertyRights
//
//  Synopsis:   Reads the specified property rights from the named DS object
//
//  Arguments:  [IN  pwszDSObj]     --  The DSObject to get the security
//                                      descriptor for
//              [IN  pRightsList]   --  The rights information to get
//              [IN  cRights]       --  Number of items in the rights list
//              [IN  AccessList]    --  The access list to initialize
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//              ERROR_INVALID_PARAMETER A NULL parameter was given
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD
ReadDSObjPropertyRights(IN  LPWSTR               pwszDSObj,
                        IN  PACTRL_RIGHTS_INFO   pRightsList,
                        IN  ULONG                cRights,
                        IN  CAccessList&         AccessList)
{
    acDebugOut((DEB_TRACE, "in ReadDSObjPropertyRights\n"));
    DWORD dwErr = ERROR_SUCCESS;

    if(pwszDSObj == NULL || pRightsList == NULL)
    {
        return(ERROR_INVALID_PARAMETER);
    }
    else
    {
        //
        // Build the security info structure we will need
        //
        SECURITY_INFORMATION SeInfo = 0;

        for(ULONG i = 0; i < cRights; i++)
        {
            SeInfo |= pRightsList[i].SeInfo;
        }

        PSECURITY_DESCRIPTOR    pSD;
        dwErr = GetSDForDSObj(pwszDSObj,
                              SeInfo,
                              &pSD);

        if(dwErr == ERROR_SUCCESS)
        {
            //
            // Now, we'll simply add the appropriate property based entries
            // to our access list.
            //
            for(ULONG iIndex = 0;
                iIndex < cRights && dwErr == ERROR_SUCCESS;
                iIndex++)
            {
                dwErr = AccessList.AddSD(pSD,
                                         pRightsList[iIndex].SeInfo,
                                         pRightsList[iIndex].pwszProperty,
                                         FALSE);

            }
            AccFree(pSD);
        }

    }

    acDebugOut((DEB_TRACE, "out ReadDSObjPropertyRights: %lu\n", dwErr));
    return(dwErr);
}




//+---------------------------------------------------------------------------
//
//  Function:   ReadAllDSObjPropertyRights
//
//  Synopsis:   Reads the all the property rights from the named DS object
//
//  Arguments:  [IN  pwszDSObj]     --  The DSObject to get the security
//                                      descriptor for
//              [IN  pRightsList]   --  The rights information to get
//              [IN  cRights]       --  Number of items in the rights list
//              [IN  AccessList]    --  The access list to initialize
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//              ERROR_INVALID_PARAMETER A NULL parameter was given
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD
ReadAllDSObjPropertyRights(IN  LPWSTR               pwszDSObj,
                           IN  PACTRL_RIGHTS_INFO   pRightsList,
                           IN  ULONG                cRights,
                           IN  CAccessList&         AccessList)
{
    DWORD dwErr = ERROR_SUCCESS;

    if(pwszDSObj == NULL || pRightsList == NULL)
    {
        return(ERROR_INVALID_PARAMETER);
    }
    else
    {
        //
        // Build the security info structure we will need
        //
        SECURITY_INFORMATION SeInfo = 0;

        for(ULONG i = 0; i < cRights; i++)
        {
            SeInfo |= pRightsList[i].SeInfo;
        }

        PSECURITY_DESCRIPTOR    pSD;
        dwErr = GetSDForDSObj(pwszDSObj,
                              SeInfo,
                              &pSD);

        if(dwErr == ERROR_SUCCESS)
        {
            //
            // Now, we'll simply add it to our access list.  We'll ignore
            // any rights info after the first one.
            //
            dwErr = AccessList.AddSD(pSD,
                                     pRightsList[0].SeInfo,
                                     NULL,
                                     TRUE);
            AccFree(pSD);
        }

    }

    return(dwErr);
}



//+---------------------------------------------------------------------------
//
//  Function:   SetDSObjSecurityInfo
//
//  Synopsis:   Sets the security descriptor on the DS object
//
//  Arguments:  [IN  pwszDSObj]     --  The DSObject to get the security
//                                      descriptor for
//              [IN  SeInfo]        --  Security Infofor the security
//                                      descriptor
//              [IN  pwszProperty]  --  Object property to set the access on
//              [IN  pSD]           --  Security descriptor to set
//              [IN  cSDSize]       --  Size of the security descriptor
//              [IN  pfStopFlag]    --  The stop flag to monitor
//              [IN  pcProcessed]   --  Where to increment the count of
//                                      processsed items
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//              ERROR_INVALID_PARAMETER A NULL parameter was given
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD
SetDSObjSecurityInfo(IN  LPWSTR                  pwszDSObj,
                     IN  SECURITY_INFORMATION    SeInfo,
                     IN  PWSTR                   pwszProperty,
                     IN  PSECURITY_DESCRIPTOR    pSD,
                     IN  ULONG                   cSDSize,
                     IN  PULONG                  pfStopFlag,
                     IN  PULONG                  pcProcessed)
{
    DWORD dwErr = ERROR_SUCCESS;
    PWSTR pwszServer = NULL, pwszPath = NULL;

    dwErr = DspSplitPath(pwszDSObj,
                         &pwszServer,
                         &pwszPath);

    if(dwErr == ERROR_SUCCESS)
    {


        //
        // Convert the name into attributed format
        //
        PDS_NAME_RESULTW pNameRes;

        dwErr = DspBindAndCrack( pwszServer, pwszPath, 0, &pNameRes );

        if(dwErr == ERROR_SUCCESS)
        {
            if(pNameRes->cItems == 0 || pNameRes->rItems[0].status != 0)
            {
                dwErr = ERROR_PATH_NOT_FOUND;
            }
            else
            {
                //
                // Convert our name to ascii
                //
                PLDAP   pLDAP;

                dwErr = BindToDSObject(pwszServer,
                                       pNameRes->rItems[0].pDomain,
                                       &pLDAP);

                if(dwErr == ERROR_SUCCESS)
                {
#ifdef NO_PROPAGATE
                    dwErr = StampSD(pNameRes->rItems[0].pName,
                                    cSDSize,
                                    SeInfo,
                                    pSD,
                                    pLDAP);
#else
                    dwErr = PropagateDSRightsDeep(NULL,
                                                  pSD,
                                                  SeInfo,
                                                  pNameRes->rItems[0].pName,
                                                  pLDAP,
                                                  pcProcessed,
                                                  pfStopFlag);
#endif
                    UnBindFromDSObject(&pLDAP);
                }

            }

            DsFreeNameResultW(pNameRes);
        }

        AccFree(pwszServer);
    }

    return(dwErr);
}




//+---------------------------------------------------------------------------
//
//  Function:   PingDSObj
//
//  Synopsis:   "Pings" the specified DS object, to determine if it is
//              reachable or not
//
//  Arguments:  [IN  pObjectName]   --  The name of the object
//
//  Returns:    ERROR_SUCCESS       --  The object is reachable
//              ERROR_PATH_NOT_FOUND--  The object was not reachable
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD
PingDSObj(IN LPCWSTR  pwszDSObj)
{
    acDebugOut((DEB_TRACE, "in PingDSObj\n"));
    DWORD dwErr;

    PWSTR pwszServer = NULL, pwszPath = NULL;

    dwErr = DspSplitPath((PWSTR)pwszDSObj,
                         &pwszServer,
                         &pwszPath);

    if(dwErr == ERROR_SUCCESS)
    {

        //
        // Convert the name into attributed format
        //
        PDS_NAME_RESULTW   pNameRes;
        dwErr = DspBindAndCrack(pwszServer, pwszPath, 0, &pNameRes);
        if(dwErr == ERROR_SUCCESS)
        {
            if(pNameRes->cItems == 0 || pNameRes->rItems[0].status != 0)
            {
                dwErr = ERROR_PATH_NOT_FOUND;
            }
            else
            {
                //
                // Now, we'll bind to the object, and then do the read
                //
                PLDAP   pLDAP;

                dwErr = BindToDSObject(pwszServer,
                                       pNameRes->rItems[0].pDomain,
                                       &pLDAP);

                if(dwErr == ERROR_SUCCESS)
                {
                    PLDAPMessage    pMessage = NULL;
                    PWSTR           rgAttribs[2];

                    rgAttribs[0] = L"distinguishedName";
                    rgAttribs[1] = NULL;


                    if(dwErr == ERROR_SUCCESS)
                    {
                        dwErr = ldap_search_s(pLDAP,
                                              pwszPath,
                                              LDAP_SCOPE_BASE,
                                              L"(objectClass=*)",
                                              rgAttribs,
                                              0,
                                              &pMessage);

                        dwErr = LdapMapErrorToWin32( dwErr );
                    }

                    if(dwErr == ERROR_SUCCESS)
                    {
                        LDAPMessage *pEntry = NULL;
                        pEntry = ldap_first_entry(pLDAP,
                                                  pMessage);

                        if(pEntry == NULL)
                        {
                            dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                        }
                        else
                        {
                            //
                            // Now, we'll have to get the values
                            //
                            PWSTR *ppwszValues = ldap_get_values(pLDAP,
                                                                 pEntry,
                                                                 rgAttribs[0]);
                            if(ppwszValues == NULL)
                            {

                                if(pLDAP->ld_errno == LDAP_NO_SUCH_ATTRIBUTE )
                                {

                                    dwErr =  ERROR_SUCCESS;
                                }
                                else
                                {
                                    dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                                }
                            }
                            else
                            {
                                ldap_value_free(ppwszValues);
                            }
                        }

                        ldap_msgfree(pMessage);
                    }
                }
            }
            DsFreeNameResultW(pNameRes);
        }

        AccFree(pwszServer);
    }
    acDebugOut((DEB_TRACE, "out PingDSObj: %lu\n", dwErr));
    return(dwErr);
}




DWORD
Nt4NameToNt5Name(IN  PWSTR      pwszName,
                 IN  PWSTR      pwszDomain,
                 OUT PWSTR     *ppwszNt5Name)
{
    DWORD   dwErr = ERROR_SUCCESS;

    WCHAR   wszFullName[MAX_PATH + 1];
    LPWSTR  pwszFullName;

    ULONG   cLen = wcslen(pwszName) + 1;

    if(pwszDomain != NULL)
    {
        cLen += wcslen(pwszDomain) + 1;
    }

    if(cLen < MAX_PATH + 1)
    {
        pwszFullName = wszFullName;
    }
    else
    {
        pwszFullName = (PWSTR)AccAlloc(cLen * sizeof(WCHAR));
        if(pwszFullName == NULL)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
        }
    }

    if(dwErr == ERROR_SUCCESS)
    {
        if(pwszDomain != NULL)
        {
            wcscpy(pwszFullName, pwszDomain);
            wcscat(pwszFullName, L"\\");
        }
        else
        {
            *pwszFullName = L'\0';
        }
        wcscat(pwszFullName, pwszName);

    }

    //
    // Now, for the crack name...
    //
    if(dwErr == ERROR_SUCCESS)
    {
        PDS_NAME_RESULTW   pNameRes;

        dwErr = DspBindAndCrack(NULL, wszFullName, 0, &pNameRes );
        if(dwErr == ERROR_SUCCESS)
        {
            if(pNameRes->cItems == 0 || pNameRes->rItems[0].status != 0)
            {
                dwErr = ERROR_PATH_NOT_FOUND;
            }
            else
            {
                ACC_ALLOC_AND_COPY_STRINGW(pNameRes->rItems[0].pName,
                                           *ppwszNt5Name,
                                           dwErr);
            }
        }
        else
        {
            if(dwErr == MAPI_E_LOGON_FAILED)
            {
                dwErr = ERROR_LOGON_FAILURE;
            }

        }

        DsFreeNameResultW(pNameRes);
    }


    //
    // See if we need to free our buffer
    //
    if(pwszFullName != wszFullName)
    {
        AccFree(pwszFullName);
    }

    return(dwErr);
}




#define CLEANUP_ON_INTERRUPT(pstopflag)                                     \
if(*pstopflag != 0)                                                         \
{                                                                           \
    goto DSCleanup;                                                         \
}
//+---------------------------------------------------------------------------
//
//  Function:   PropagateDSRightsDeep, recursive
//
//  Synopsis:   Does a deep propagation of the access.
//
//  Arguments:  [IN  pParentSD]         --      The current parent sd
//              [IN  SeInfo]            --      What is being written
//              [IN  pwszFile]          --      Parent file path
//              [IN  pcProcessed]       --      Where the number processed is
//                                              returned.
//              [IN  pfStopFlag]        --      Stop flag to monitor
//
//  Returns:    ERROR_SUCCESS           --      Success
//              ERROR_NOT_ENOUGH_MEMORY --      A memory allocation failed
//
//----------------------------------------------------------------------------
DWORD
PropagateDSRightsDeep(IN  PSECURITY_DESCRIPTOR    pParentSD,
                      IN  PSECURITY_DESCRIPTOR    pChildSD,
                      IN  SECURITY_INFORMATION    SeInfo,
                      IN  PWSTR                   pwszDSObject,
                      IN  PLDAP                   pLDAP,
                      IN  PULONG                  pcProcessed,
                      IN  PULONG                  pfStopFlag)
{
    acDebugOut((DEB_TRACE, "in PropagateDSRightsDeep\n"));
    DWORD   dwErr = ERROR_SUCCESS;
    BOOL    fFreeChildSD = FALSE;


    acDebugOut((DEB_TRACE_PROP, "Processing %ws\n", pwszDSObject));

    //
    // If our security descriptor is already in DS form, then we'll have
    // to adjust for that
    //
    if(pChildSD != NULL)
    {
        PULONG pSE = (PULONG)(pChildSD);
        if(*pSE == SeInfo)
        {
            pChildSD = (PSECURITY_DESCRIPTOR)((PBYTE)pChildSD + sizeof(ULONG));
        }
    }
    else
    {
        dwErr = ReadDSObjSecDesc(pLDAP,
                                 pwszDSObject,
                                 SeInfo,
                                 &pChildSD);
        if(dwErr == ERROR_SUCCESS)
        {
            fFreeChildSD = TRUE;
        }
        else
        {
            return(dwErr);
        }
    }

    //
    // Ok, we'll convert our path to a narrow string, and then we'll enumerate
    // all of the children
    //
    PSECURITY_DESCRIPTOR    pNewSD = NULL;
    PLDAPMessage            pMessage = NULL;

    //
    // First, we'll create the new SD...
    //
    HANDLE                  hProcessToken = NULL;
    GENERIC_MAPPING GenMap;
    GenMap.GenericRead    = GENERIC_READ_MAPPING;
    GenMap.GenericWrite   = GENERIC_WRITE_MAPPING;
    GenMap.GenericExecute = GENERIC_EXECUTE_MAPPING;
    GenMap.GenericAll     = GENERIC_ALL_MAPPING;

    dwErr = GetCurrentToken( &hProcessToken );
    if(dwErr == ERROR_SUCCESS)
    {
#ifdef DBG
        DebugDumpSD("CPOSE ParentSD", pParentSD);
        DebugDumpSD("CPOSE ChildSD",  pChildSD);
#endif
        if(CreatePrivateObjectSecurityEx(pParentSD,
                                         pChildSD,
                                         &pNewSD,
                                         NULL,
                                         TRUE,
                                         SEF_DACL_AUTO_INHERIT |
                                            SEF_SACL_AUTO_INHERIT,
                                         hProcessToken,
                                         &GenMap) == FALSE)
        {
            dwErr = GetLastError();
        }
        else
        {
#ifdef DBG
            DebugDumpSD("CPOSE NewSD", pNewSD);
#endif
            //
            // Stamp the SD on the object...  This means that we'll have
            // to allocate a new security descriptor that is 4 bytes
            // bigger than what we need, and set our SeInfo
            //
            PSECURITY_DESCRIPTOR    pSetSD = NULL;
            ULONG cNewSDSize = 0;

            if(RtlpAreControlBitsSet((PISECURITY_DESCRIPTOR)pChildSD,
                                     SE_SELF_RELATIVE))
            {
                cNewSDSize = RtlLengthSecurityDescriptor(pNewSD);
                ASSERT(cNewSDSize != 0);
            }
            else
            {
                MakeSelfRelativeSD(pNewSD,
                                   NULL,
                                   &cNewSDSize);
                ASSERT(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
            }

            cNewSDSize += sizeof(ULONG);

            pSetSD = (PSECURITY_DESCRIPTOR)AccAlloc(cNewSDSize);
            if(pSetSD == NULL)
            {
                dwErr = ERROR_NOT_ENOUGH_MEMORY;
            }
            else
            {
                pSetSD = (PSECURITY_DESCRIPTOR)
                               ((PBYTE)pSetSD + sizeof(ULONG));

                if(RtlpAreControlBitsSet((PISECURITY_DESCRIPTOR)pChildSD,
                                         SE_SELF_RELATIVE))
                {
                    memcpy(pSetSD, pNewSD, cNewSDSize - sizeof(ULONG));
                }
                else
                {
                    if(MakeSelfRelativeSD(pNewSD,
                                          pSetSD,
                                          &cNewSDSize) == FALSE)
                    {
                        dwErr = GetLastError();
                    }
                }


                PULONG pSE = (PULONG)((PBYTE)pSetSD - sizeof(ULONG));
                *pSE = SeInfo;

                //
                // We need to pass in the security_information
                //
                pSetSD = (PSECURITY_DESCRIPTOR)pSE;


                //
                // Now, do the write
                //
                dwErr = StampSD(pwszDSObject,
                                cNewSDSize,
                                SeInfo,
                                pSetSD,
                                pLDAP);
                AccFree(pSetSD);
            }
        }
    }


    CLEANUP_ON_INTERRUPT(pfStopFlag);
    if(dwErr == ERROR_SUCCESS)
    {
        PWSTR            rgAttribs[2];
        WCHAR            wszAttrib[]=L"distinguishedName";

        //
        // Do the search...
        //
        rgAttribs[0] = wszAttrib;
        rgAttribs[1] = NULL;


        if(dwErr == ERROR_SUCCESS)
        {
            dwErr = ldap_search_s(pLDAP,
                                  pwszDSObject,
                                  LDAP_SCOPE_ONELEVEL,
                                  L"(objectClass=*)",
                                  rgAttribs,
                                  0,
                                  &pMessage);

            dwErr = LdapMapErrorToWin32( dwErr );
        }

        if(dwErr == ERROR_SUCCESS)
        {

            ULONG   cChildren = ldap_count_entries(pLDAP,
                                                   pMessage);
            acDebugOut((DEB_TRACE_PROP,
                        "%ws has %lu children\n",
                        pwszDSObject, cChildren));

            LDAPMessage *pEntry = ldap_first_entry(pLDAP,
                                                   pMessage);
            for(ULONG i = 0; i < cChildren; i++)
            {
                if(pEntry == NULL)
                {
                    dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                    break;
                }

                //
                // Now, we'll have to get the values
                //
                CLEANUP_ON_INTERRUPT(pfStopFlag);
                PWSTR  *ppwszValues = ldap_get_values(pLDAP,
                                                      pEntry,
                                                      rgAttribs[0]);

                if(ppwszValues == NULL)
                {
                    dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                }
                else
                {
                    //
                    // Go ahead and propagate to the child
                    //
                    acDebugOut((DEB_TRACE_PROP,
                                "Child %ws of %ws [%lu]\n",
                                ppwszValues[0], pwszDSObject, i));

                    dwErr = PropagateDSRightsDeep(pNewSD,
                                                  NULL,
                                                  SeInfo,
                                                  ppwszValues[0],
                                                  pLDAP,
                                                  pcProcessed,
                                                  pfStopFlag);

                    ldap_value_free(ppwszValues);
                    CLEANUP_ON_INTERRUPT(pfStopFlag);
                }

                pEntry = ldap_next_entry(pLDAP,
                                         pEntry);
            }
        }
    }

DSCleanup:
    ldap_msgfree(pMessage);
    DestroyPrivateObjectSecurity(&pNewSD);

    if(fFreeChildSD == TRUE)
    {
        AccFree(pChildSD);
    }

    acDebugOut((DEB_TRACE, "Out PropagateDSRightsDeep: %ld\n", dwErr));
    return(dwErr);

}





//+---------------------------------------------------------------------------
//
//  Function:   StampSD
//
//  Synopsis:   Actually stamps the security descriptor on the object.
//
//  Arguments:  [IN  pwszObject]        --      The object to stamp the SD on
//              [IN  cSDSize]           --      The size of the security descriptor
//              [IN  SeInfo]            --      SecurityInformation about the security
//                                              descriptor
//              [IN  pSD]               --      The SD to stamp
//              [IN  pLDAP]             --      The LDAP connection to use
//
//  Returns:    ERROR_SUCCESS           --      Success
//              ERROR_NOT_ENOUGH_MEMORY --      A memory allocation failed
//
//----------------------------------------------------------------------------
DWORD
StampSD(IN  PWSTR                pwszObject,
        IN  ULONG                cSDSize,
        IN  SECURITY_INFORMATION SeInfo,
        IN  PSECURITY_DESCRIPTOR pSD,
        IN  PLDAP                pLDAP)
{
    DWORD   dwErr = ERROR_SUCCESS;

    acDebugOut((DEB_TRACE_PROP, "Stamping %ws\n", pwszObject));

    //
    // Now, we'll do the write.  The security descriptor
    // we got passed in better not be in the old Ds  format,
    // where the leading 4 bytes are the SECURITY_INFORMATION, which we'll skip
    // and replace with control information
    //

    ASSERT(*(PULONG)pSD > 0xF );

    PLDAPMod        rgMods[2];
    PLDAP_BERVAL    pBVals[2];
    LDAPMod         Mod;
    LDAP_BERVAL     BVal;
    BYTE            ControlBuffer[ 5 ];

    LDAPControl     SeInfoControl =
                    {
                        LDAP_SERVER_SD_FLAGS_OID_W,
                        {
                            5, (PCHAR) &ControlBuffer
                        },
                        TRUE
                    };

    //
    // !!! Hardcoded for now.  Use Andyhe's BER_printf once it's done.
    //

    ControlBuffer[0] = 0x30;
    ControlBuffer[1] = 0x3;
    ControlBuffer[2] = 0x02;    // Denotes an integer;
    ControlBuffer[3] = 0x01;    // Size
    ControlBuffer[4] = (BYTE)((ULONG)SeInfo & 0xF);

    PLDAPControl    ServerControls[2] =
                    {
                        &SeInfoControl,
                        NULL
                    };

    ASSERT(IsValidSecurityDescriptor( pSD ) );


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

    pBVals[0] = &BVal;
    pBVals[1] = NULL;

    BVal.bv_len = cSDSize;
    BVal.bv_val = (PCHAR)pSD;

    Mod.mod_op      = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
    Mod.mod_type    = ACTRL_SD_PROP_NAME;
    Mod.mod_values  = (PWSTR *)pBVals;

    //
    // Now, we'll do the write...
    //
    dwErr = ldap_modify_ext_s(pLDAP,
                              pwszObject,
                              rgMods,
                              (PLDAPControl *)&ServerControls,
                              NULL);

    dwErr = LdapMapErrorToWin32(dwErr);

#if DBG
    PACL pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR)pSD);
    ACL_SIZE_INFORMATION        AclSize;
    ACL_REVISION_INFORMATION    AclRev;
    PKNOWN_ACE                  pAce;
    PSID                        pSid;
    DWORD                       iIndex;
    DWORD                       GuidPart;
    DWORD                       OldInfoLevel;

    //
    // Now, dump all of the aces
    //
    if(pAcl)
    {
        pAce = (PKNOWN_ACE)FirstAce(pAcl);
        for(iIndex = 0; iIndex < pAcl->AceCount; iIndex++)
        {
            //
            // If it's an object ace, dump the guids
            //
            if(IsObjectAceType(pAce))
            {
                OldInfoLevel = acInfoLevel;
//                acInfoLevel |= DEB_TRACE_SD;
                DebugDumpGuid("\t\t\tObjectId", RtlObjectAceObjectType(pAce));
                GuidPart = (ULONG)((ULONG_PTR)RtlObjectAceObjectType(pAce));
                ASSERT(GuidPart != 0x2bfff20);
                acInfoLevel = OldInfoLevel;
            }
            pAce = (PKNOWN_ACE)NextAce(pAce);
        }
    }
#endif

    return(dwErr);
}


//+---------------------------------------------------------------------------
//
//  Function:   AccDsReadSchemaInfo
//
//  Synopsis:   Reads the schema object/property info.
//
//  Arguments:  [IN  pLDAP]                 --      LDAP connection to use
//              [OUT pcClasses]             --      Where the count of class info
//                                                  is returned
//              [OUT pppwszClasses]         --      Where the list of classes info
//                                                  is returned.  Freed with
//                                                  ldap_value_free
//              [OUT pcAttributes]          --      Where the count of property infos
//                                                  is returned
//              [OUT pppwszAttributes]      --      Where the list of property infos
//                                                  is returned.  Freed with
//                                                  ldap_value_free
//
//  Returns:    ERROR_SUCCESS               --      Success
//
//----------------------------------------------------------------------------
DWORD
AccDsReadSchemaInfo (IN  PLDAP          pLDAP,
                     OUT PULONG         pcClasses,
                     OUT PWSTR        **pppwszClasses,
                     OUT PULONG         pcAttributes,
                     OUT PWSTR        **pppwszAttributes)
{
    DWORD               dwErr = ERROR_SUCCESS;
    PWSTR              *ppwszValues = NULL;
    PWSTR               rgwszAttribs[3];
    PDS_NAME_RESULTW    pNameRes = NULL;
    LDAPMessage         *pMessage, *pEntry;

    *pcClasses    = 0;
    *pcAttributes = 0;
    *pppwszAttributes = NULL;
    *pppwszClasses = NULL;

    //
    // Get the subschema path
    //
    if(dwErr == ERROR_SUCCESS)
    {
        rgwszAttribs[0] = L"subschemaSubentry";
        rgwszAttribs[1] = NULL;

        dwErr = ldap_search_s(pLDAP,
                              NULL,
                              LDAP_SCOPE_BASE,
                              L"(objectClass=*)",
                              rgwszAttribs,
                              0,
                              &pMessage);
        if(dwErr == ERROR_SUCCESS)
        {
            pEntry = ldap_first_entry(pLDAP,
                                      pMessage);

            if(pEntry == NULL)
            {
                dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
            }
            else
            {
                //
                // Now, we'll have to get the values
                //
                ppwszValues = ldap_get_values(pLDAP,
                                              pEntry,
                                              rgwszAttribs[0]);
                ldap_msgfree(pMessage);

                if(ppwszValues == NULL)
                {
                    dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                }
                else
                {
                    rgwszAttribs[0] = L"extendedClassInfo";
                    rgwszAttribs[1] = L"extendedAttributeInfo";
                    rgwszAttribs[2] = NULL;

                    dwErr = ldap_search_s(pLDAP,
                                          ppwszValues[0],
                                          LDAP_SCOPE_BASE,
                                          L"(objectClass=*)",
                                          rgwszAttribs,
                                          0,
                                          &pMessage);
                    ldap_value_free(ppwszValues);

                    if(dwErr == ERROR_SUCCESS)
                    {
                        ldap_count_entries( pLDAP, pMessage );
                        pEntry = ldap_first_entry(pLDAP,
                                                  pMessage);

                        if(pEntry == NULL)
                        {
                            dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                        }
                        else
                        {
                            //
                            // Now, we'll have to get the values
                            //
                            *pppwszClasses = ldap_get_values(pLDAP,
                                                             pEntry,
                                                             rgwszAttribs[0]);
                            if(*pppwszClasses == NULL)
                            {
                                dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                            }
                            else
                            {
                                *pcClasses = ldap_count_values(*pppwszClasses);

                                *pppwszAttributes = ldap_get_values(pLDAP,
                                                                    pEntry,
                                                                    rgwszAttribs[1]);
                                if(*pppwszAttributes == NULL)
                                {
                                    dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );

                                    ldap_value_free(*pppwszClasses);
                                }
                                else
                                {
                                    *pcAttributes =
                                            ldap_count_values(*pppwszAttributes);
                                }
                            }
                        }

                    }
                    else
                    {
                        dwErr = LdapMapErrorToWin32( dwErr );
                    }

                    ldap_msgfree(pMessage);
                }
            }

        }
        else
        {
            dwErr = LdapMapErrorToWin32( dwErr );
        }

    }

    return(dwErr);
}




//+---------------------------------------------------------------------------
//
//  Function:   AccDsReadExtendedRights
//
//  Synopsis:   Reads the list of extended rights from the schema
//
//  Arguments:  [IN  pLDAP]                 --      LDAP connection to use
//              [OUT pcItems]               --      Where the count of items
//                                                  is returned
//              [OUT ppwszNames]            --      Where the list of Names is
//                                                  returned.
//              [OUT ppwszGuids]            --      Where the list of guids is
//                                                  returned.
//
//  Notes:      Freed via AccDsFreeExtendedRights
//
//  Returns:    ERROR_SUCCESS               --      Success
//
//----------------------------------------------------------------------------
DWORD
AccDsReadExtendedRights(IN PLDAP        pLDAP,
                        OUT PULONG      pcItems,
                        OUT PWSTR     **pppwszNames,
                        OUT PWSTR     **pppwszGuids)
{
    DWORD               dwErr = ERROR_SUCCESS;
    PWSTR              *ppwszValues = NULL;
    PWSTR               rgwszAttribs[3];
    PWSTR               pwszERContainer = NULL;
    PDS_NAME_RESULTW    pNameRes = NULL;
    LDAPMessage         *pMessage, *pEntry;
    ULONG               cEntries = 0, i;

    *pcItems = 0;
    *pppwszNames = NULL;
    *pppwszGuids = NULL;

    //
    // Get the subschema path
    //
    if(dwErr == ERROR_SUCCESS)
    {
        rgwszAttribs[0] = L"configurationNamingContext";
        rgwszAttribs[1] = NULL;

        dwErr = ldap_search_s(pLDAP,
                              NULL,
                              LDAP_SCOPE_BASE,
                              L"(objectClass=*)",
                              rgwszAttribs,
                              0,
                              &pMessage);
        if(dwErr == ERROR_SUCCESS)
        {
            pEntry = ldap_first_entry(pLDAP,
                                      pMessage);

            if(pEntry == NULL)
            {
                dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
            }
            else
            {
                //
                // Now, we'll have to get the values
                //
                ppwszValues = ldap_get_values(pLDAP,
                                              pEntry,
                                              rgwszAttribs[0]);
                ldap_msgfree(pMessage);

                if(ppwszValues == NULL)
                {
                    dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                }
                else
                {
                    pwszERContainer = (PWSTR)AccAlloc((wcslen(ppwszValues[0]) * sizeof(WCHAR)) +
                                                      sizeof(ACTRL_EXT_RIGHTS_CONTAINER));
                    if(pwszERContainer == NULL)
                    {
                        dwErr = ERROR_NOT_ENOUGH_MEMORY;
                    }
                    else
                    {
                        wcscpy(pwszERContainer,
                               ACTRL_EXT_RIGHTS_CONTAINER);
                        wcscat(pwszERContainer,
                               ppwszValues[0]);


                        rgwszAttribs[0] = L"displayName";
                        rgwszAttribs[1] = L"rightsGuid";
                        rgwszAttribs[2] = NULL;

                        //
                        // Read the control access rights
                        //
                        dwErr = ldap_search_s(pLDAP,
                                              pwszERContainer,
                                              LDAP_SCOPE_ONELEVEL,
                                              L"(objectClass=controlAccessRight)",
                                              rgwszAttribs,
                                              0,
                                              &pMessage);

                        dwErr = LdapMapErrorToWin32( dwErr );

                        AccFree(pwszERContainer);
                    }
                    ldap_value_free(ppwszValues);


                    if(dwErr == ERROR_SUCCESS)
                    {
                        cEntries = ldap_count_entries( pLDAP, pMessage );

                        *pppwszNames = (PWSTR *)AccAlloc( sizeof( PWSTR ) * cEntries );

                        if(*pppwszNames == NULL)
                        {
                            dwErr = ERROR_NOT_ENOUGH_MEMORY;
                        }
                        else
                        {
                            *pppwszGuids = (PWSTR *)AccAlloc( sizeof( PWSTR ) * cEntries );

                            if(*pppwszGuids == NULL)
                            {
                                dwErr = ERROR_NOT_ENOUGH_MEMORY;
                                AccFree(*pppwszNames);
                                *pppwszNames = NULL;
                            }
                        }

                        if(dwErr == ERROR_SUCCESS)
                        {
                            pEntry = ldap_first_entry(pLDAP,
                                                      pMessage);

                            if(pEntry == NULL)
                            {
                                dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                            }
                            else
                            {
                                for(i = 0; i < cEntries && dwErr == ERROR_SUCCESS; i++) {
                                    ppwszValues = ldap_get_values(pLDAP,
                                                                  pEntry,
                                                                  rgwszAttribs[0]);
                                    if(ppwszValues == NULL)
                                    {
                                        dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
                                    }
                                    else
                                    {
                                        //
                                        // Now, we'll have to get the values
                                        //
                                        ACC_ALLOC_AND_COPY_STRINGW(ppwszValues[0],
                                                                   (*pppwszNames)[i],
                                                                   dwErr);

                                        ldap_value_free(ppwszValues);

                                        if(dwErr == ERROR_SUCCESS)
                                        {
                                            ppwszValues = ldap_get_values(pLDAP,
                                                                          pEntry,
                                                                          rgwszAttribs[1]);
                                            if(ppwszValues == NULL)
                                            {
                                                dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );

                                            }
                                            else
                                            {
                                                ACC_ALLOC_AND_COPY_STRINGW(ppwszValues[0],
                                                                           (*pppwszGuids)[i],
                                                                           dwErr);
                                                ldap_value_free(ppwszValues);
                                            }
                                        }
                                    }

                                    pEntry = ldap_next_entry( pLDAP, pEntry );
                                }

                                if(dwErr != ERROR_SUCCESS)
                                {
                                    AccDsFreeExtendedRights(i,
                                                            *pppwszNames,
                                                            *pppwszGuids);
                                }
                            }
                        }

                    }

                    if(dwErr == ERROR_SUCCESS)
                    {
                        *pcItems = cEntries;
                    }

                    ldap_msgfree(pMessage);
                }
            }

        }
        else
        {
            dwErr = LdapMapErrorToWin32( dwErr );
        }

    }

    return(dwErr) ;
}




VOID
AccDsFreeExtendedRights(IN ULONG      cItems,
                        IN PWSTR     *ppwszNames,
                        IN PWSTR     *ppwszGuids)
{
    ULONG i;

    for(i = 0;i < cItems;i++ )
    {
        AccFree( ppwszNames[ i ]);
        AccFree( ppwszGuids[ i ]);
    }

    AccFree(ppwszNames);
    AccFree(ppwszGuids);
}