//+---------------------------------------------------------------------------
//
//  Microsoft Windows NT Security
//  Copyright (C) Microsoft Corporation, 1997 - 1998
//
//  File:       dsctx.cpp
//
//  Contents:   Implementation of CDsObjectContext and NT Marta DS object
//              Functions
//
//  History:    3-31-1999    kirtd    Created
//
//----------------------------------------------------------------------------
#include <aclpch.hxx>
#pragma hdrstop

#include <windows.h>
#include <dsctx.h>
//+---------------------------------------------------------------------------
//
//  Member:     CDsObjectContext::CDsObjectContext, public
//
//  Synopsis:   Constructor
//
//----------------------------------------------------------------------------
CDsObjectContext::CDsObjectContext ()
{
    m_cRefs = 1;
    memset( &m_LdapUrlComponents, 0, sizeof( m_LdapUrlComponents ) );
    m_pBinding = NULL;
}

//+---------------------------------------------------------------------------
//
//  Member:     CDsObjectContext::~CDsObjectContext, public
//
//  Synopsis:   Destructor
//
//----------------------------------------------------------------------------
CDsObjectContext::~CDsObjectContext ()
{
    LdapFreeBindings( m_pBinding );
    LdapFreeUrlComponents( &m_LdapUrlComponents );

    assert( m_cRefs == 0 );
}

//+---------------------------------------------------------------------------
//
//  Member:     CDsObjectContext::InitializeByName, public
//
//  Synopsis:   initialize the context given the name of the lanman share
//
//----------------------------------------------------------------------------
DWORD
CDsObjectContext::InitializeByName (LPCWSTR pObjectName, ACCESS_MASK AccessMask)
{
    DWORD  Result = ERROR_SUCCESS;
    LPWSTR pwszName = NULL;
    ULONG  len = wcslen( pObjectName );
    ULONG  i, j;

    if ( _wcsnicmp( pObjectName, LDAP_SCHEME_U, wcslen( LDAP_SCHEME_U ) ) != 0 )
    {
        pwszName = new WCHAR [ len + wcslen( LDAP_SCHEME_UC ) + 2 ];

        if ( pwszName != NULL )
        {
            wcscpy( pwszName, LDAP_SCHEME_UC );
            wcscat( pwszName, L"/" );
            wcscat( pwszName, pObjectName );
        }
        else
        {
            Result = ERROR_OUTOFMEMORY;
        }
    }
    else
    {
        pwszName = new WCHAR [ len + 1 ];

        if ( pwszName != NULL )
        {
            wcscpy( pwszName, pObjectName );
        }
        else
        {
            Result = ERROR_OUTOFMEMORY;
        }
    }

    if ( Result == ERROR_SUCCESS )
    {
        for (i = j = 0; i <= len; i++, j++)
        {
            if (L'\\' == pwszName[i])
            {
                if (L'/' != pwszName[i+1])
                {
                    pwszName[j++] = pwszName[i++];
                }
                else
                {
                    i++;
                }
            }
            pwszName[j] = pwszName[i];
        }
    }

    if ( Result == ERROR_SUCCESS )
    {
        if ( LdapCrackUrl( pwszName, &m_LdapUrlComponents ) == FALSE )
        {
            Result = GetLastError();
        }
    }

    if ( Result == ERROR_SUCCESS )
    {
        if ( LdapGetBindings(
                 m_LdapUrlComponents.pwszHost,
                 m_LdapUrlComponents.Port,
                 0,
                 0,
                 &m_pBinding
                 ) == FALSE )
        {
            Result = GetLastError();
        }
    }

    if ( pwszName != pObjectName )
    {
        delete pwszName;
    }

    return( Result );
}

//+---------------------------------------------------------------------------
//
//  Member:     CDsObjectContext::AddRef, public
//
//  Synopsis:   add a reference to the context
//
//----------------------------------------------------------------------------
DWORD
CDsObjectContext::AddRef ()
{
    m_cRefs += 1;
    return( m_cRefs );
}

//+---------------------------------------------------------------------------
//
//  Member:     CDsObjectContext::Release, public
//
//  Synopsis:   release a reference to the context
//
//----------------------------------------------------------------------------
DWORD
CDsObjectContext::Release ()
{
    m_cRefs -= 1;

    if ( m_cRefs == 0 )
    {
        delete this;
        return( 0 );
    }

    return( m_cRefs );
}

//+---------------------------------------------------------------------------
//
//  Member:     CDsObjectContext::GetDsObjectProperties, public
//
//  Synopsis:   get properties about the context
//
//----------------------------------------------------------------------------
DWORD
CDsObjectContext::GetDsObjectProperties (
                    PMARTA_OBJECT_PROPERTIES pObjectProperties
                    )
{
    if ( pObjectProperties->cbSize < sizeof( MARTA_OBJECT_PROPERTIES ) )
    {
        return( ERROR_INVALID_PARAMETER );
    }

    assert( pObjectProperties->dwFlags == 0 );

    return( ERROR_SUCCESS );
}

//+---------------------------------------------------------------------------
//
//  Member:     CDsObjectContext::GetDsObjectRights, public
//
//  Synopsis:   get the DsObject security descriptor
//
//----------------------------------------------------------------------------
DWORD
CDsObjectContext::GetDsObjectRights (
                    SECURITY_INFORMATION SecurityInfo,
                    PSECURITY_DESCRIPTOR* ppSecurityDescriptor
                    )
{
    DWORD Result;

    Result = MartaReadDSObjSecDesc(
                 m_pBinding,
                 m_LdapUrlComponents.pwszDN,
                 SecurityInfo,
                 ppSecurityDescriptor
                 );

    return( Result );
}

//+---------------------------------------------------------------------------
//
//  Member:     CDsObjectContext::SetDsObjectRights, public
//
//  Synopsis:   set the window security descriptor
//
//----------------------------------------------------------------------------
DWORD
CDsObjectContext::SetDsObjectRights (
                   SECURITY_INFORMATION SecurityInfo,
                   PSECURITY_DESCRIPTOR pSecurityDescriptor
                   )
{
    DWORD                 Result;
    PISECURITY_DESCRIPTOR pisd = NULL;
    DWORD                 cb = 0;
    PSECURITY_DESCRIPTOR  psd = NULL;

    pisd = (PISECURITY_DESCRIPTOR)pSecurityDescriptor;

    if ( pisd->Control & SE_SELF_RELATIVE )
    {
        cb = GetSecurityDescriptorLength( pSecurityDescriptor );
        psd = pSecurityDescriptor;
    }
    else
    {
        if ( MakeSelfRelativeSD(
                 pSecurityDescriptor,
                 NULL,
                 &cb
                 ) == FALSE )
        {
            if ( cb > 0 )
            {
                psd = new BYTE [ cb ];
                if ( psd != NULL )
                {
                    if ( MakeSelfRelativeSD(
                             pSecurityDescriptor,
                             psd,
                             &cb
                             ) == FALSE )
                    {
                        delete psd;
                        return( GetLastError() );
                    }
                }
                else
                {
                    return( ERROR_OUTOFMEMORY );
                }
            }
            else
            {
                return( GetLastError() );
            }
        }
        else
        {
            assert( FALSE && "Should not get here!" );
            return( ERROR_INVALID_PARAMETER );
        }
    }

    assert( psd != NULL );

    Result = MartaStampSD(
                  m_LdapUrlComponents.pwszDN,
                  cb,
                  SecurityInfo,
                  psd,
                  m_pBinding
                  );

    if ( psd != pSecurityDescriptor )
    {
        delete psd;
    }

    return( Result );
}

//+---------------------------------------------------------------------------
//
//  Member:     CDsObjectContext::GetDsObjectGuid, public
//
//  Synopsis:   get the object GUID
//
//----------------------------------------------------------------------------
DWORD
CDsObjectContext::GetDsObjectGuid (GUID* pGuid)
{
    return( ERROR_INVALID_PARAMETER );
}

//
// Functions from Ds.h which dispatch unto the CDsObjectContext class
//

DWORD
MartaAddRefDsObjectContext(
   IN MARTA_CONTEXT Context
   )
{
    return( ( (CDsObjectContext *)Context )->AddRef() );
}

DWORD
MartaCloseDsObjectContext(
     IN MARTA_CONTEXT Context
     )
{
    return( ( (CDsObjectContext *)Context )->Release() );
}

DWORD
MartaGetDsObjectProperties(
   IN MARTA_CONTEXT Context,
   IN OUT PMARTA_OBJECT_PROPERTIES pProperties
   )
{
    return( ( (CDsObjectContext *)Context )->GetDsObjectProperties( pProperties ) );
}

DWORD
MartaGetDsObjectTypeProperties(
   IN OUT PMARTA_OBJECT_TYPE_PROPERTIES pProperties
   )
{
    if ( pProperties->cbSize < sizeof( MARTA_OBJECT_TYPE_PROPERTIES ) )
    {
        return( ERROR_INVALID_PARAMETER );
    }

    assert( pProperties->dwFlags == 0 );

    pProperties->dwFlags = MARTA_OBJECT_TYPE_INHERITANCE_MODEL_PRESENT_FLAG;

    return( ERROR_SUCCESS );
}

DWORD
MartaGetDsObjectRights(
   IN  MARTA_CONTEXT Context,
   IN  SECURITY_INFORMATION   SecurityInfo,
   OUT PSECURITY_DESCRIPTOR * ppSecurityDescriptor
   )
{
    return( ( (CDsObjectContext *)Context )->GetDsObjectRights(
                                               SecurityInfo,
                                               ppSecurityDescriptor
                                               ) );
}

DWORD
MartaOpenDsObjectNamedObject(
    IN  LPCWSTR pObjectName,
    IN  ACCESS_MASK AccessMask,
    OUT PMARTA_CONTEXT pContext
    )
{
    DWORD           Result;
    CDsObjectContext* pDsObjectContext;

    pDsObjectContext = new CDsObjectContext;
    if ( pDsObjectContext == NULL )
    {
        return( ERROR_OUTOFMEMORY );
    }

    Result = pDsObjectContext->InitializeByName( pObjectName, AccessMask );
    if ( Result != ERROR_SUCCESS )
    {
        pDsObjectContext->Release();
        return( Result );
    }

    *pContext = pDsObjectContext;
    return( ERROR_SUCCESS );
}

DWORD
MartaSetDsObjectRights(
    IN MARTA_CONTEXT              Context,
    IN SECURITY_INFORMATION SecurityInfo,
    IN PSECURITY_DESCRIPTOR pSecurityDescriptor
    )
{
    return( ( (CDsObjectContext *)Context )->SetDsObjectRights(
                                               SecurityInfo,
                                               pSecurityDescriptor
                                               ) );
}

DWORD
MartaConvertDsObjectNameToGuid(
    IN LPCWSTR pObjectName,
    OUT GUID* pGuid
    )
{
    DWORD               Result = ERROR_SUCCESS;
    LPWSTR              pwszName = NULL;
    LDAP_URL_COMPONENTS LdapUrlComponents;
    DS_NAME_RESULTW*    pnameresult;
    HANDLE              hDs = NULL;
    WCHAR               GuidString[ MAX_PATH ];

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

    if ( _wcsnicmp( pObjectName, LDAP_SCHEME_U, wcslen( LDAP_SCHEME_U ) ) != 0 )
    {
        pwszName = new WCHAR [ wcslen( pObjectName ) +
                               wcslen( LDAP_SCHEME_U ) + 2 ];

        if ( pwszName != NULL )
        {
            wcscpy( pwszName, LDAP_SCHEME_U );
            wcscat( pwszName, L"/" );
            wcscat( pwszName, pObjectName );
        }
        else
        {
            Result = ERROR_OUTOFMEMORY;
        }
    }
    else
    {
        pwszName = (LPWSTR)pObjectName;
    }

    if ( Result == ERROR_SUCCESS )
    {
        if ( LdapCrackUrl( pwszName, &LdapUrlComponents ) == FALSE )
        {
            Result = GetLastError();
        }
    }

    if ( Result == ERROR_SUCCESS )
    {
        Result = DsBindW( LdapUrlComponents.pwszHost, NULL, &hDs );
    }

    if ( Result == ERROR_SUCCESS )
    {
        Result = DsCrackNamesW(
                   hDs,
                   DS_NAME_NO_FLAGS,
                   DS_FQDN_1779_NAME,
                   DS_UNIQUE_ID_NAME,
                   1,
                   &LdapUrlComponents.pwszDN,
                   &pnameresult
                   );
    }

    if ( Result == ERROR_SUCCESS )
    {
        if ( ( pnameresult->cItems > 0 ) &&
             ( pnameresult->rItems[0].status == ERROR_SUCCESS ) )
        {
            Result = IIDFromString( pnameresult->rItems[0].pName, pGuid );
        }
        else
        {
            Result = ERROR_INVALID_PARAMETER;
        }

        DsFreeNameResultW( pnameresult );
    }

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

    LdapFreeUrlComponents( &LdapUrlComponents );

    if ( pwszName != pObjectName )
    {
        delete pwszName;
    }

    return( Result );
}

DWORD
MartaConvertGuidToDsName(
    IN  GUID     Guid,
    OUT LPWSTR * ppObjectName
    )
{
    DWORD            Result;
    HANDLE           hDs = NULL;
    WCHAR            GuidString[ MAX_PATH ];
    DS_NAME_RESULTW* pnameresult = NULL;
    LPWSTR           pObjectName = NULL;

    if ( StringFromGUID2( Guid, GuidString, MAX_PATH ) == 0 )
    {
        return( ERROR_INVALID_PARAMETER );
    }

    Result = DsBindW( NULL, NULL, &hDs );

    if ( Result == ERROR_SUCCESS )
    {
        Result = DsCrackNamesW(
                   hDs,
                   DS_NAME_NO_FLAGS,
                   DS_UNIQUE_ID_NAME,
                   DS_FQDN_1779_NAME,
                   1,
                   (LPCWSTR *)&GuidString,
                   &pnameresult
                   );
    }

    if ( Result == ERROR_SUCCESS )
    {
        if ( ( pnameresult->cItems > 0 ) &&
             ( pnameresult->rItems[0].status == ERROR_SUCCESS ) )
        {
            pObjectName = (LPWSTR)LocalAlloc(
                                       LPTR,
                                       ( wcslen( pnameresult->rItems[0].pName )
                                         + 1 ) * sizeof( WCHAR )
                                       );

            if ( pObjectName != NULL )
            {
                wcscpy( pObjectName, pnameresult->rItems[0].pName );
                *ppObjectName = pObjectName;
            }
            else
            {
                Result = ERROR_OUTOFMEMORY;
            }
        }
        else
        {
            Result = ERROR_INVALID_PARAMETER;
        }

        DsFreeNameResultW( pnameresult );
    }

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

    return( ERROR_SUCCESS );
}

//+---------------------------------------------------------------------------
//
//  Function:   MartaReadDSObjSecDesc
//
//  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
MartaReadDSObjSecDesc(IN  PLDAP                  pLDAP,
                      IN  LPWSTR                 pwszObject,
                      IN  SECURITY_INFORMATION   SeInfo,
                      OUT PSECURITY_DESCRIPTOR  *ppSD)
{
    DWORD   dwErr = ERROR_SUCCESS;

    PLDAPMessage    pMessage = NULL;
    LPWSTR           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);

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

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

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



    if(dwErr == ERROR_SUCCESS)
    {
        dwErr = ldap_search_ext_sW(pLDAP,
                                   pwszObject,
                                   LDAP_SCOPE_BASE,
                                   L"(objectClass=*)",
                                   rgAttribs,
                                   0,
                                   (PLDAPControlW *)&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 );
            if (ERROR_SUCCESS == dwErr)
                dwErr = ERROR_ACCESS_DENIED;
        }
        else
        {
            PLDAP_BERVAL *pSize = ldap_get_values_lenW(pLDAP,
                                                       pMessage,
                                                       rgAttribs[0]);
            if(pSize == NULL)
            {
                dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
            }
            else
            {
                //
                // Allocate the security descriptor to return
                //
                *ppSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, (*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);
            }
        }
    }

    if ( pMessage != NULL )
    {
        ldap_msgfree(pMessage);
    }

    return(dwErr);
}

//+---------------------------------------------------------------------------
//
//  Function:   MartaStampSD
//
//  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
MartaStampSD(IN  LPWSTR               pwszObject,
             IN  ULONG                cSDSize,
             IN  SECURITY_INFORMATION SeInfo,
             IN  PSECURITY_DESCRIPTOR pSD,
             IN  PLDAP                pLDAP)
{
    DWORD   dwErr = ERROR_SUCCESS;

    //
    // 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 );

    PLDAPModW       rgMods[2];
    PLDAP_BERVAL    pBVals[2];
    LDAPModW        Mod;
    LDAP_BERVAL     BVal;
    BYTE            ControlBuffer[ 5 ];

    LDAPControlW     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);

    PLDAPControlW    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    = SD_PROP_NAME;
    Mod.mod_values  = (LPWSTR *)pBVals;

    //
    // Now, we'll do the write...
    //

    dwErr = ldap_modify_ext_sW(pLDAP,
                               pwszObject,
                               rgMods,
                               (PLDAPControlW *)&ServerControls,
                               NULL);

    dwErr = LdapMapErrorToWin32(dwErr);

    return(dwErr);
}

DWORD
MartaGetDsParentName(
    IN LPWSTR ObjectName,
    OUT LPWSTR *pParentName
    )

/*++

Routine Description:

    Given the name of a DS object return the name of its parent. The routine
    allocates memory required to hold the parent name.

Arguments:

    ObjectName - Name of the DS object.

    pParentName - To return the pointer to the allocated parent name.
        In case of the root of the tree, we return NULL parent with ERROR_SUCCESS.

Return Value:

    ERROR_SUCCESS in case of success.
    ERROR_* otherwise

--*/

{
    LPCWSTR pKey  = NULL;
    LPCWSTR pVal  = NULL;
    DWORD  ccKey = 0;
    DWORD  ccDN  = 0;
    DWORD  ccVal = 0;
    DWORD  Size  = 0;
    DWORD  dwErr = ERROR_SUCCESS;
    LPCWSTR pDN   = (LPWSTR) ObjectName;

    ccDN = wcslen(pDN);
    *pParentName = NULL;

    //
    // The input is empty. There is no parent. Just return.
    //

    if (0 == ccDN)
    {
        return ERROR_SUCCESS;
    }

    //
    // Do the first pass to get to the next level. At the end of this call,
    // pDN will point to the next ','. One more call to DsGetRdnW will
    // return the right result in pKey.
    // Input:
    //   pDN = "CN=Kedar, DC=NTDEV, DC=Microsoft, DC=com"
    // Output:
    //   pDN = ", DC=NTDEV, DC=Microsoft, DC=com"
    //

    dwErr = DsGetRdnW(
                &pDN,
                &ccDN,
                &pKey,
                &ccKey,
                &pVal,
                &ccVal
                );

     if (ERROR_SUCCESS != dwErr)
     {
         return dwErr;
     }

     //
     // This is TRUE when the Object does not have any parent.
     //

     if (0 == ccDN)
     {
         return ERROR_SUCCESS;
     }

     //
     // Input:
     //   pDN = ", DC=NTDEV, DC=Microsoft, DC=com"
     // Output:
     //   pKey = "DC=NTDEV, DC=Microsoft, DC=com"
     //

     dwErr = DsGetRdnW(
                 &pDN,
                 &ccDN,
                 &pKey,
                 &ccKey,
                 &pVal,
                 &ccVal
                 );

     if (ERROR_SUCCESS != dwErr)
     {
         return dwErr;
     }

     //
     // We have to distinguish between LDAP://ServerName/ObjectName and
     // ObjectName.
     //

     if (!_wcsnicmp(ObjectName, LDAP_SCHEME_U, wcslen(LDAP_SCHEME_U)) != 0 )
     {
         ULONG HostSize;

         //
         // Compute the size of string required to hold "LDAP//ServerName/" in
         // HostSize.
         //

         pDN = ObjectName + sizeof("ldap://");
         pDN = wcschr(pDN, L'/');

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

         HostSize = (ULONG) (pDN - ObjectName + 1);

         Size = (1 + wcslen(pKey) + HostSize) * sizeof(WCHAR);

         *pParentName = (LPWSTR) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, Size);

         if (NULL == *pParentName)
         {
             return ERROR_NOT_ENOUGH_MEMORY;
         }

         //
         // Copy the name of the parent into allocated memeory.
         //

         wcsncpy(*pParentName, ObjectName, HostSize);
         wcscpy((*pParentName) + HostSize, pKey);
     }
     else
     {
         Size = (1 + wcslen(pKey)) * sizeof(WCHAR);

         *pParentName = (LPWSTR) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, Size);

         if (NULL == *pParentName)
         {
             return ERROR_NOT_ENOUGH_MEMORY;
         }

         //
         // Copy the name of the parent into allocated memeory.
         //

         wcscpy(*pParentName, pKey);
     }


     return ERROR_SUCCESS;
}