//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1993 - 1994.
//
//  File:    registry.cxx
//
//  Contents:    local functions
//
//  History:    8/94    davemont    Created
//
//----------------------------------------------------------------------------
#include <aclpch.hxx>
#pragma hdrstop
#include <alsup.hxx>
#include <martaevt.h>

//
// Registry generic mapping
//
GENERIC_MAPPING         gRegGenMapping = {STANDARD_RIGHTS_READ     | 0x1,
                                          STANDARD_RIGHTS_WRITE    | 0x2,
                                          STANDARD_RIGHTS_EXECUTE  | 0x4,
                                          STANDARD_RIGHTS_REQUIRED | 0x3F};



//+---------------------------------------------------------------------------
//
//  Function : GetDesiredAccess
//
//  Synopsis : Gets the access required to open object to be able to set or
//             get the specified security info.
//
//  Arguments: IN [SecurityOpenType]  - Flag indicating if the object is to be
//                                      opened to read or write the DACL
//
//----------------------------------------------------------------------------
ACCESS_MASK RegGetDesiredAccess(IN SECURITY_OPEN_TYPE   OpenType,
                                IN SECURITY_INFORMATION SecurityInfo)
{
    acDebugOut((DEB_TRACE_ACC, "in GetDesiredAccess \n"));

    ACCESS_MASK DesiredAccess = 0;

    if ( (SecurityInfo & OWNER_SECURITY_INFORMATION) ||
         (SecurityInfo & GROUP_SECURITY_INFORMATION) )
    {
        switch (OpenType)
        {
        case READ_ACCESS_RIGHTS:
            DesiredAccess |= READ_CONTROL;
            break;
        case WRITE_ACCESS_RIGHTS:
            DesiredAccess |= WRITE_OWNER;
            break;
        case MODIFY_ACCESS_RIGHTS:
            DesiredAccess |= READ_CONTROL | WRITE_OWNER;
            break;
        }
    }

    if (SecurityInfo & DACL_SECURITY_INFORMATION)
    {
        switch (OpenType)
        {
        case READ_ACCESS_RIGHTS:
            DesiredAccess |= READ_CONTROL;
            break;
        case WRITE_ACCESS_RIGHTS:
            DesiredAccess |= WRITE_DAC;
            break;
        case MODIFY_ACCESS_RIGHTS:
            DesiredAccess |= READ_CONTROL | WRITE_DAC;
            break;
        }
    }

    if (SecurityInfo & SACL_SECURITY_INFORMATION)
    {
        DesiredAccess |= READ_CONTROL | ACCESS_SYSTEM_SECURITY;
    }

    acDebugOut((DEB_TRACE_ACC, "out RegGetDesiredAccess: %lu\n", DesiredAccess));

    return (DesiredAccess);
}


//+---------------------------------------------------------------------------
//
//  Function:   OpenRegistryObject
//
//  Synopsis:   Opens the specified registry object
//
//  Arguments:  [IN pwszRegistry]       --      The name of the registry key
//                                              to open
//              [IN AccessMask]         --      Flags indicating if the object
//                                              is to be opened to read or write
//                                              the DACL
//              [OUT pHandle]           --      Where the open handle is
//                                              returned
//
//  Returns:    ERROR_SUCCESS           --      Success
//              ERROR_NOT_ENOUGH_MEMORY --      A memory allocation failed
//              ERROR_INVALID_PARAMETER --      A bad name was given
//
//----------------------------------------------------------------------------
DWORD
OpenRegistryObject(IN  LPWSTR       pwszRegistry,
                   IN  ACCESS_MASK  AccessMask,
                   OUT PHANDLE      pHandle)
{
    acDebugOut((DEB_TRACE, "in OpenRegistryObject\n"));

    DWORD dwErr;
    HKEY  hBase;

    if(pwszRegistry != NULL)
    {
        WCHAR   wszName[MAX_PATH + 1];
        PWSTR   pwszName;
        //
        // save the object since we must crack it to go to remote machines
        //
        dwErr = AccGetBufferOfSizeW(pwszRegistry,
                                    wszName,
                                    &pwszName);
        if(dwErr == ERROR_SUCCESS)
        {
            PWSTR   pwszRemaining, pwszMachine;

            //
            // Separate the names
            //
            dwErr = ParseName(pwszName,
                              &pwszMachine,
                              &pwszRemaining);

            if(dwErr == ERROR_SUCCESS)
            {
                PWSTR   pwszKey = NULL;
                //
                // look for the key names  localization required.
                //
                if (pwszRemaining != NULL)
                {
                    PWSTR   pwszBase = pwszRemaining;
                    pwszKey  = wcschr(pwszRemaining, L'\\');
                    if(pwszKey != NULL)
                    {
                        *pwszKey = L'\0';
                        pwszKey++;
                    }

                    //
                    // Now, figure out what our base key will be
                    //
                    if(_wcsicmp(pwszBase, L"MACHINE") == 0)
                    {
                        hBase = HKEY_LOCAL_MACHINE;
                    }
                    else if(_wcsicmp(pwszBase, L"USERS") == 0 ||
                            _wcsicmp(pwszBase, L"USER") == 0 )
                    {
                        hBase = HKEY_USERS;
                    }
                    //
                    // The next three are valid only for the local machine
                    //
                    else if(pwszMachine == NULL &&
                            _wcsicmp(pwszBase, L"CLASSES_ROOT") == 0)
                    {
                        hBase = HKEY_CLASSES_ROOT;
                    }
                    else if(pwszMachine == NULL &&
                            _wcsicmp(pwszBase,L"CURRENT_USER") == 0)
                    {
                        hBase = HKEY_CURRENT_USER;
                    }
                    else if(pwszMachine == NULL &&
                            _wcsicmp(pwszBase, L"CONFIG") == 0)
                    {
                        hBase = HKEY_CURRENT_CONFIG;
                    }
                    else
                    {
                        dwErr = ERROR_INVALID_PARAMETER;
                    }
                }
                else
                {
                    dwErr = ERROR_INVALID_PARAMETER;
                }

                if(dwErr == ERROR_SUCCESS)
                {
                    //
                    // if it is a remote name, connect to that registry
                    //
                    if(pwszMachine != NULL)
                    {
                        HKEY hMach = hBase;
                        dwErr = RegConnectRegistry(pwszMachine,
                                                   hMach,
                                                   &hBase);
                    }

                    //
                    // Now, open the key
                    //
                    if(dwErr == ERROR_SUCCESS)
                    {
                        dwErr = RegOpenKeyEx(hBase,
                                             pwszKey,
                                             0,
                                             AccessMask,
                                             (PHKEY)pHandle);

                        if(pwszMachine != NULL)
                        {
                            RegCloseKey(hBase);
                        }
                    }
                }
            }

            AccFreeBufferOfSizeW(wszName, pwszName);
        }
    }
    else
    {
        dwErr = ERROR_INVALID_PARAMETER;
    }

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




//+---------------------------------------------------------------------------
//
//  Function:   ReadRegistryPropertyRights
//
//  Synopsis:   Gets the specified security info for the specified registry
//              object
//
//  Arguments:  [IN  pwszRegistry]      --      The reg key to get the rights
//                                              for
//              [IN  pRightsList]       --      SecurityInfo to read based
//                                              on properties
//              [IN  cRights]           --      Number of items in rights list
//              [IN  AccessList]        --      Access List to fill in
//
//  Returns:    ERROR_SUCCESS           --      Success
//              ERROR_INVALID_PARAMETER --      A bad property was encountered
//              ERROR_NOT_ENOUGH_MEMORY --      A memory allocation failed
//
//----------------------------------------------------------------------------
DWORD
ReadRegistryPropertyRights(IN  LPWSTR                pwszRegistry,
                           IN  PACTRL_RIGHTS_INFO    pRightsList,
                           IN  ULONG                 cRights,
                           IN  CAccessList&          AccessList)
{
    acDebugOut((DEB_TRACE, "in ReadRegistryPropertyRights\n"));

    HANDLE hReg;
    DWORD  dwErr;

    //
    // Currently, there are only registry object properties
    //
    ASSERT(cRights == 1 && pRightsList[0].pwszProperty == NULL);
    if(cRights != 1 || pRightsList[0].pwszProperty != NULL)
    {
        return(ERROR_INVALID_PARAMETER);
    }

    //
    // Set the lookup server name
    //
    dwErr = SetAccessListLookupServer( pwszRegistry,
                                       AccessList );

    //
    // Open the registry key
    //
    if(dwErr == ERROR_SUCCESS)
    {
        dwErr = OpenRegistryObject(pwszRegistry,
                                   RegGetDesiredAccess(READ_ACCESS_RIGHTS,
                                                       pRightsList[0].SeInfo),
                                   &hReg);
    }

    if(dwErr == ERROR_SUCCESS)
    {
        PSECURITY_DESCRIPTOR    pSD;
        dwErr = ReadRegistrySecurityInfo(hReg,
                                         pRightsList[0].SeInfo,
                                         &pSD);
        //
        // If that worked, we'll have to get the parent SD, if it exists,
        // and see if we can determine the inheritance on our current object
        //
        if(dwErr == ERROR_SUCCESS)
        {
            if((pRightsList[0].SeInfo & ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) != 0 &&
                !FLAG_ON(((PISECURITY_DESCRIPTOR)pSD)->Control,
                         SE_SACL_AUTO_INHERITED |
                            SE_DACL_AUTO_INHERITED))
            {
                //
                // Ok, it's downlevel, so get the parent SD...
                //
                PSECURITY_DESCRIPTOR    pParentSD;
                dwErr = GetRegistryParentRights(pwszRegistry,
                                                pRightsList[0].SeInfo,
                                                &pParentSD);

                //
                // Also, the routine to convert from nt4 to nt5 security
                // descriptor requires that we have the owner and group,
                // so we may have to reread the child SD if we don't have
                // that info
                //
                if(dwErr == ERROR_SUCCESS && (!FLAG_ON(pRightsList[0].SeInfo,
                                            OWNER_SECURITY_INFORMATION)  ||
                                            !FLAG_ON(pRightsList[0].SeInfo,
                                            GROUP_SECURITY_INFORMATION)))
                {
                    AccFree(pSD);
                    pSD = NULL;
                    dwErr = ReadRegistrySecurityInfo(hReg,
                                                     pRightsList[0].SeInfo          |
                                                        OWNER_SECURITY_INFORMATION  |
                                                        GROUP_SECURITY_INFORMATION,
                                                     &pSD);
                }

                //
                // A NULL parent SD means this object has no parent!
                //
                if(dwErr == ERROR_SUCCESS && pParentSD != NULL)
                {
                    PSECURITY_DESCRIPTOR    pNewSD;
                    dwErr = ConvertToAutoInheritSD(pParentSD,
                                                   pSD,
                                                   TRUE,
                                                   &gRegGenMapping,
                                                   &pNewSD);
                    if(dwErr == ERROR_SUCCESS)
                    {
                        dwErr = AccessList.AddSD(pNewSD,
                                                 pRightsList[0].SeInfo,
                                                 pRightsList[0].pwszProperty);

                        DestroyPrivateObjectSecurity(&pNewSD);
                    }

                    AccFree(pParentSD);
                }
            }
            else
            {
                //
                // Simply add the SD to our list
                //
                dwErr = AccessList.AddSD(pSD,
                                         pRightsList[0].SeInfo,
                                         pRightsList[0].pwszProperty);

            }

            //
            // Make sure to free the security descriptor...
            //
            AccFree(pSD);
        }


        RegCloseKey((HKEY)hReg);
    }


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




//+---------------------------------------------------------------------------
//
//  Function:   ReadRegistryRights
//
//  Synopsis:   Gets the specified security info for the specified registry
//              object
//
//  Arguments:  [IN  hRegistry]         --      Reg handle to get the rights
//                                              for
//              [IN  pRightsList]       --      SecurityInfo to read based
//                                              on properties
//              [IN  cRights]           --      Number of items in rights list
//              [IN  AccessList]        --      Access List to fill in
//
//  Returns:    ERROR_SUCCESS           --      Success
//              ERROR_INVALID_PARAMETER --      A bad property was encountered
//              ERROR_NOT_ENOUGH_MEMORY --      A memory allocation failed
//
//----------------------------------------------------------------------------
DWORD
ReadRegistryRights(IN  HANDLE                hRegistry,
                   IN  PACTRL_RIGHTS_INFO    pRightsList,
                   IN  ULONG                 cRights,
                   IN  CAccessList&          AccessList)
{
    acDebugOut((DEB_TRACE, "in ReadRegistryRights\n"));

    DWORD  dwErr;

    //
    // Currently, there are only registry object properties
    //
    ASSERT(cRights == 1 && pRightsList[0].pwszProperty == NULL);
    if(cRights != 1 || pRightsList[0].pwszProperty != NULL)
    {
        return(ERROR_INVALID_PARAMETER);
    }


    PSECURITY_DESCRIPTOR    pSD = NULL;

    dwErr = ReadRegistrySecurityInfo(hRegistry,
                                     pRightsList[0].SeInfo,
                                     &pSD);
    if((dwErr != ERROR_SUCCESS) || (pSD == NULL))
    {
        return(dwErr);
    }

    //
    // Take a look at it... If it's a downlevel object, let's reread it as an uplevel, if
    // possible
    //
    if(!FLAG_ON(((PISECURITY_DESCRIPTOR)pSD)->Control,
                  SE_SACL_AUTO_INHERITED |
                               SE_DACL_AUTO_INHERITED))
    {
        PWSTR   pwszRegPath = NULL;

        dwErr = ConvertRegHandleToName((HKEY)hRegistry,
                                       &pwszRegPath);
        if(dwErr != ERROR_SUCCESS)
        {
            if(dwErr == ERROR_INVALID_HANDLE)
            {
                //
                // It's remote, so add it as is...
                //
                dwErr = AccessList.AddSD(pSD,
                                         pRightsList->SeInfo,
                                         pRightsList->pwszProperty);
            }
        }
        else
        {
            dwErr = ReadRegistryPropertyRights(pwszRegPath,
                                               pRightsList,
                                               cRights,
                                               AccessList);
            AccFree(pwszRegPath);
        }

    }
    else
    {
        //
        // It's already uplevel, so add it as is...
        //
        dwErr = AccessList.AddSD(pSD,
                                 pRightsList->SeInfo,
                                 pRightsList->pwszProperty);
    }

    AccFree(pSD);

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




//+---------------------------------------------------------------------------
//
//  Function:   ReadRegistrySecurityInfo
//
//  Synopsis:   Reads the specified security info for the handle's registry
//              key object
//
//  Arguments:  [IN  hRegistry]         --      The handle to the object to
//                                              get the rights for
//              [IN  SeInfo]            --      SecurityInfo to read based
//              [OUT ppOwner]           --      The owner sid
//              [OUT ppGroup]           --      The group sid
//              [OUT pDAcl]             --      The DACL
//              [OUT pSAcl]             --      The SACL
//              [OUT pSD]               --      The security descriptor itself
//
//  Returns:    ERROR_SUCCESS           --      Success
//              ERROR_NOT_ENOUGH_MEMORY --      A memory allocation failed
//
//----------------------------------------------------------------------------
DWORD
ReadRegistrySecurityInfo(IN  HANDLE                 hRegistry,
                         IN  SECURITY_INFORMATION   SeInfo,
                         OUT PSECURITY_DESCRIPTOR  *ppSD)
{
    acDebugOut((DEB_TRACE, "in ReadRegistrySecurityInfo \n"));

    ULONG                   cSize = 0;
    DWORD                   dwErr;

    //
    // First, get the size we need
    //
    dwErr = RegGetKeySecurity((HKEY)hRegistry,
                              SeInfo,
                              *ppSD,
                              &cSize);
    if(dwErr == ERROR_INSUFFICIENT_BUFFER)
    {
        dwErr = ERROR_SUCCESS;
        *ppSD = (PISECURITY_DESCRIPTOR)AccAlloc(cSize);
        if(*ppSD == NULL)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
        }
        else
        {
            dwErr = RegGetKeySecurity((HKEY)hRegistry,
                                      SeInfo,
                                      *ppSD,
                                      &cSize);


            if(dwErr == ERROR_SUCCESS &&
                FLAG_ON(((SECURITY_DESCRIPTOR *)*ppSD)->Control,SE_SELF_RELATIVE))
            {
                PSECURITY_DESCRIPTOR    pAbs;
                dwErr = MakeSDAbsolute(*ppSD,
                                       SeInfo,
                                       &pAbs);
                if(dwErr == ERROR_SUCCESS)
                {
                    AccFree(*ppSD);
                    *ppSD = pAbs;
                }
            }
        }
    }
    else

    {
        ASSERT(dwErr != ERROR_INSUFFICIENT_BUFFER);
    }


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




//+---------------------------------------------------------------------------
//
//  Function:   GetRegistryParentRights
//
//  Synopsis:   Determines who the parent is, and gets the access rights
//              for it.  It is used to aid in determining what the approriate
//              inheritance bits are.
//
//              This operation does not make sense for kernel objects
//
//  Arguments:  [IN  pwszRegistry]      --      The reg path to get the parent
//                                              for
//              [IN  SeInfo]            --      The security information to do
//                                              the read for
//              [OUT ppDAcl]            --      Where the DACL is returned
//              [OUT ppSAcl]            --      Where the SACL is returned
//              [OUT ppSD]              --      Where the Security Descriptor
//                                              is returned
//
//  Returns:    ERROR_SUCCESS           --      Success
//
//
//----------------------------------------------------------------------------
DWORD
GetRegistryParentRights(IN  LPWSTR                    pwszRegistry,
                        IN  SECURITY_INFORMATION      SeInfo,
                        OUT PSECURITY_DESCRIPTOR     *ppSD)
{
    DWORD   dwErr = ERROR_SUCCESS;
    //
    // Basically, we'll figure out who our parent is, and get their info
    //
    PWSTR   pwszLastComp = wcsrchr(pwszRegistry, L'\\');
    if(pwszLastComp == NULL)
    {
        //
        // Ok, we must be at the root, so we won't have any inheritance
        //
        //
        // Return success after nulling out SD.
        //
        *ppSD = NULL;
    }
    else
    {
        //
        // We'll shorten our path, and then get the info
        //
        *pwszLastComp = L'\0';

        HANDLE  hReg;

        //
        // Don't want owner or group
        //
        SeInfo &= ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION);
        dwErr = OpenRegistryObject(pwszRegistry,
                                   RegGetDesiredAccess(READ_ACCESS_RIGHTS,SeInfo),
                                   &hReg);

        if(dwErr == ERROR_SUCCESS)
        {
            dwErr = ReadRegistrySecurityInfo(hReg,
                                             SeInfo,
                                             ppSD);
            RegCloseKey((HKEY)hReg);
        }

        *pwszLastComp = L'\\';

    }

    return(dwErr);
}




//+---------------------------------------------------------------------------
//
//  Function:   SetRegistrySecurityInfo
//
//  Synopsis:   Sets the specified security info on the specified registry
//              object
//
//  Arguments:  [IN  hService]          --      The handle of the object
//              [IN  SeInfo]            --      Flag indicating what security
//                                              info to set
//              [IN  pwszProperty]      --      The property on the object to
//                                              set
//                                              For kernel objects, this MBZ
//              [IN  pSD]               --      The security descriptor to set
//
//  Returns:    ERROR_SUCCESS           --      Success
//              ERROR_INVALID_PARAMETER --      A bad property was given
//
//----------------------------------------------------------------------------
DWORD
SetRegistrySecurityInfo(IN  HANDLE                    hRegistry,
                        IN  SECURITY_INFORMATION      SeInfo,
                        IN  PWSTR                     pwszProperty,
                        IN  PSECURITY_DESCRIPTOR      pSD)
{
    acDebugOut((DEB_TRACE, "in SetNamedRegistrySecurityInfo\n"));

    DWORD dwErr;

    //
    // Registry keys don't have properties
    //
    if(pwszProperty != NULL)
    {
        dwErr = ERROR_INVALID_PARAMETER;
    }
    else
    {
        //
        // Marta only writes uplevel security descriptors.
        //
        // The caller of SetRegistrySecurityInfo will call with SE_xACL_AUTO_INHERITED off in those
        //  cases that it wants the underlying registry to do auto inheritance.
        // The caller of SetRegistrySecurityInfo will call with SE_xACL_AUTO_INHERITED on in those
        //  cases that it wants the underlying registry to simply store the bits.
        //
        // In the later case, the OS uses the SE_xACL_AUTO_INHERIT_REQ bit as a flag indicating
        // that it is OK to preserve SE_xACL_AUTO_INHERITED bit.
        //
        if(FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION)) {
            ((PISECURITY_DESCRIPTOR)pSD)->Control |= SE_DACL_AUTO_INHERIT_REQ;
        }

        if(FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION)) {
            ((PISECURITY_DESCRIPTOR)pSD)->Control |= SE_SACL_AUTO_INHERIT_REQ;
        }

        dwErr = RegSetKeySecurity((HKEY)hRegistry,
                                  SeInfo,
                                  pSD);

    }

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




#define CLEANUP_ON_INTERRUPT(pstopflag)                                     \
if(*pstopflag != 0)                                                         \
{                                                                           \
    goto RegCleanup;                                                        \
}
//+---------------------------------------------------------------------------
//
//  Function:   SetAndPropagateRegistryPropertyRights
//
//  Synopsis:   Sets the access on the given registry path and propagates
//              it as necessary
//
//  Arguments:  [IN  pwszRegistry]      --      The path to set and propagate
//              [IN  pwszProperty]      --      The registry property to
//                                              operate upon
//              [IN  RootAccList]       --      The CAccessList class that has
//                                              the security descriptor/info
//              [IN  pfStopFlag]        --      Address of the stop flag
//                                              to be monitored
//              [IN  pcProcessed]       --      count of processed items to
//                                              be incremented.
//
//  Returns:    ERROR_SUCCESS           --      Success
//              ERROR_INVALID_PARAMETER --      A bad paramter was given
//
//----------------------------------------------------------------------------
DWORD
SetAndPropagateRegistryPropertyRights(IN  PWSTR                 pwszRegistry,
                                      IN  PWSTR                 pwszProperty,
                                      IN  CAccessList&          RootAccList,
                                      IN  PULONG                pfStopFlag,
                                      IN  PULONG                pcProcessed)
{
    acDebugOut((DEB_TRACE, "in SetAndPropagateRegistryPropertyRights\n"));

    DWORD                   dwErr = ERROR_SUCCESS;

    //
    // First, get our security descriptor and sec info
    //
    HKEY                    hReg = NULL;
    PSECURITY_DESCRIPTOR    pSD = NULL;
    PSECURITY_DESCRIPTOR    pParentSD = NULL;
    SECURITY_INFORMATION    SeInfo = 0;

    dwErr = RootAccList.BuildSDForAccessList(&pSD,
                                             &SeInfo,
                                             ACCLIST_SD_ABSOK);
    if(dwErr == ERROR_SUCCESS)
    {
        //
        // Next, open the registry
        //
        dwErr = OpenRegistryObject(pwszRegistry,
                                   RegGetDesiredAccess(MODIFY_ACCESS_RIGHTS,
                                                    SeInfo)             |
                                        KEY_ENUMERATE_SUB_KEYS          |
                                        KEY_QUERY_VALUE,
                                   (PHANDLE)&hReg);
        if(dwErr == ERROR_SUCCESS)
        {
            //
            // Next, get our parent security descriptor
            //
            //
            // If we are only setting the owner or group, we don't need to get the parent
            //
            if (FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION) ||
                FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION) ) {

                dwErr = GetRegistryParentRights(pwszRegistry,
                                                SeInfo,
                                                &pParentSD);
            }

            if(dwErr == ERROR_SUCCESS)
            {
                //
                // Make the call
                //
                dwErr = SetAndPropRegRights(hReg,
                                            pwszRegistry,
                                            SeInfo,
                                            pParentSD,
                                            pSD,
                                            pfStopFlag,
                                            pcProcessed);
            }

        }

    }

    //
    // Clean up
    //
    if(hReg != NULL)
    {
        RegCloseKey(hReg);
    }

    AccFree(pParentSD);

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



//+---------------------------------------------------------------------------
//
//  Function:   SetAndPropagateRegistryPropertyRightsByHandle
//
//  Synopsis:   Same as above, but assumes the registry key has already
//              been opened
//
//  Arguments:  [IN  hReg]              --      The registry key to use
//              [IN  RootAccList]       --      The CAccessList class that has
//                                              the security descriptor/info
//              [IN  pfStopFlag]        --      Address of the stop flag
//                                              to be monitored
//              [IN  pcProcessed]       --      count of processed items to
//                                              be incremented.
//
//  Returns:    ERROR_SUCCESS           --      Success
//              ERROR_INVALID_PARAMETER --      A bad paramter was given
//
//----------------------------------------------------------------------------
DWORD
SetAndPropagateRegistryPropertyRightsByHandle(IN  HKEY          hReg,
                                              IN  CAccessList&  RootAccList,
                                              IN  PULONG        pfStopFlag,
                                              IN  PULONG        pcProcessed)
{
    acDebugOut((DEB_TRACE, "in SetAndPropagateRegistryPropertyRightsByHandle\n"));

    DWORD                   dwErr = ERROR_SUCCESS;
    PSECURITY_DESCRIPTOR    pParentSD = NULL;
    HANDLE                  hObject = NULL;
    BOOL                    fUplevelAcl = TRUE;
    PWSTR                   pwszRegPath = NULL;

    //
    // First, get our security descriptor and sec info
    //
    PSECURITY_DESCRIPTOR    pSD = NULL;
    SECURITY_INFORMATION    SeInfo = 0;

    dwErr = RootAccList.BuildSDForAccessList(&pSD,
                                             &SeInfo,
                                             ACCLIST_SD_ABSOK);
    if(dwErr == ERROR_SUCCESS)
    {
        //
        // If we are only setting the owner or group, we don't need to get the parent
        //
        if (FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION) ||
            FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION) ) {

            dwErr = ConvertRegHandleToName(hReg,
                                           &pwszRegPath);
            if((dwErr != ERROR_SUCCESS) || (pwszRegPath == NULL))
            {
                if(dwErr == ERROR_INVALID_HANDLE)
                {
                    dwErr = ERROR_SUCCESS;
                }
            }
            else
            {
                dwErr = GetRegistryParentRights(pwszRegPath,
                                                SeInfo,
                                                &pParentSD);
            }
        }


        if(dwErr == ERROR_SUCCESS)
        {
            //
            // Make the call
            //
            dwErr = SetAndPropRegRights(hReg,
                                        NULL,
                                        SeInfo,
                                        pParentSD,
                                        pSD,
                                        pfStopFlag,
                                        pcProcessed);

            if(dwErr == ERROR_ACCESS_DENIED)
            {
                //
                // See if we can reopen the path adding in readcontrol, and try it all again
                //
                if(pwszRegPath == NULL)
                {
                    dwErr = ConvertRegHandleToName(hReg,
                                                   &pwszRegPath);
                }

                if(pwszRegPath != NULL)
                {
                    dwErr = SetAndPropagateRegistryPropertyRights(pwszRegPath,
                                                                  NULL,
                                                                  RootAccList,
                                                                  pfStopFlag,
                                                                  pcProcessed);
                }

            }
        }
    }

    //
    // Clean up
    //
    AccFree(pwszRegPath);
    AccFree(pParentSD);

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



//+---------------------------------------------------------------------------
//
//  Function:   PropagateRegRightsDeep, recursive
//
//  Synopsis:   Does a deep propagation of the access.  At the same time, it
//              will update NT4 acls to NT5 acls.  This function is only
//              called on downlevel registries, so the update will always
//              happen (where appropriate).  The algorithm is:
//                  - Read the current security descriptor from the object
//                  - If it's a downlevel acl, update it using the OLD
//                    parent security descriptor (to set any inheritied aces)
//                  - Update the security descriptor using the NEW parent
//                    security descriptor.
//                  - Repeat for its children.  (This is necessar, since there
//                    could have been unmarked inheritance off of the old
//                    security descriptor)
//
//  Arguments:  [IN  pOldParentSD]      --      The previous parent SD (before
//                                              the current parent SD was
//                                              stamped on the object)
//              [IN  pParentSD]         --      The current parent sd
//              [IN  SeInfo]            --      What is being written
//              [IN  hParent]           --      Opened parent registry key
//              [IN  pcProcessed]       --      Where the number processed is
//                                              returned.
//              [IN  pfStopFlag]        --      Stop flag to monitor
//              [IN  fProtectedFlag]    --      Determines whether the acls are already
//                                              protected
//              [IN  hProcessToken]     --      Handle to the process token
//              [IN  LogList]           --      List of keys to which propagation failed
//
//  Returns:    ERROR_SUCCESS           --      Success
//              ERROR_INVALID_PARAMETER --      A bad paramter was given
//
//----------------------------------------------------------------------------
DWORD
PropagateRegRightsDeep(IN  PSECURITY_DESCRIPTOR    pOldParentSD,
                       IN  PSECURITY_DESCRIPTOR    pParentSD,
                       IN  SECURITY_INFORMATION    SeInfo,
                       IN  HKEY                    hParent,
                       IN  PULONG                  pcProcessed,
                       IN  PULONG                  pfStopFlag,
                       IN  ULONG                   fProtectedFlag,
                       IN  HANDLE                  hProcessToken,
                       IN OUT CSList&              LogList)
{
    acDebugOut((DEB_TRACE, "in  PropagteRegRightsDeep\n"));

    DWORD                   dwErr = ERROR_SUCCESS;
    SECURITY_DESCRIPTOR    *pChildSD = NULL;
    PSECURITY_DESCRIPTOR    pNewSD = NULL;
    BOOL                    fUpdateChild = FALSE;   // Write out the child?
    BOOL                    fAccFreeChild = TRUE;   // How to free the child

    //
    // Check to see if we've reached full protection saturation
    //
    if(fProtectedFlag == (SE_DACL_PROTECTED | SE_SACL_PROTECTED))
    {
        acDebugOut((DEB_TRACE_PROP, "Parent is fully or effectively protected\n"));
        return(ERROR_SUCCESS);
    }


    HKEY    hChild = NULL;

    ULONG    cSubKeys;
    dwErr = RegQueryInfoKey(hParent,
                            NULL,
                            NULL,
                            NULL,
                            &cSubKeys,
                            NULL,
                            NULL,
                            NULL,
                            NULL,
                            NULL,
                            NULL,
                            NULL);

    CLEANUP_ON_INTERRUPT(pfStopFlag);


    if(dwErr == ERROR_SUCCESS && cSubKeys != 0)
    {
        WCHAR   wszBuff[MAX_PATH + 1];

        ULONG       iIndex = 0;
        ULONG       cSize;
        FILETIME    WriteTime;
        while(dwErr == ERROR_SUCCESS)
        {
            cSize = MAX_PATH + 1;
            dwErr = RegEnumKeyEx(hParent,
                                 iIndex,
                                 wszBuff,
                                 &cSize,
                                 NULL,
                                 NULL,
                                 NULL,
                                 &WriteTime);
            if(dwErr == ERROR_NO_MORE_ITEMS)
            {
                dwErr = ERROR_SUCCESS;
                break;
            }

            acDebugOut((DEB_TRACE_PROP,"Propagating to %ws\n", wszBuff));

            CLEANUP_ON_INTERRUPT(pfStopFlag);

            //
            // Now, determine if we need to propagate or not...
            //
            if(dwErr == ERROR_SUCCESS)
            {
                ULONG                   cSDLen = 0;
                BOOL                    fWriteSD = FALSE;

                dwErr = RegOpenKeyEx(hParent,
                                     wszBuff,
                                     0,
                                     RegGetDesiredAccess(MODIFY_ACCESS_RIGHTS,
                                                      SeInfo)           |
                                     KEY_ENUMERATE_SUB_KEYS             |
                                     KEY_QUERY_VALUE,
                                     &hChild);

                if(dwErr == ERROR_SUCCESS)
                {
                    //
                    // Get our number of children
                    //
                    dwErr = RegQueryInfoKey(hChild,
                                            NULL,
                                            NULL,
                                            NULL,
                                            &cSubKeys,
                                            NULL,
                                            NULL,
                                            NULL,
                                            NULL,
                                            NULL,
                                            NULL,
                                            NULL);

                    if ( dwErr == ERROR_INSUFFICIENT_BUFFER ) {

                        acDebugOut((DEB_ERROR,"RegQueryInfoKey failure on %ws\n", wszBuff));
                    }

                    if(dwErr == ERROR_SUCCESS)
                    {
                        CLEANUP_ON_INTERRUPT(pfStopFlag);

                        //
                        // Read the current security descriptor
                        //
                        dwErr = ReadRegistrySecurityInfo(hChild,
                                                         SeInfo,
                                                         (PSECURITY_DESCRIPTOR *)&pChildSD);

                        CLEANUP_ON_INTERRUPT(pfStopFlag);

                        if(dwErr == ERROR_SUCCESS &&
                           !(FLAG_ON(pChildSD->Control,
                                     SE_DACL_AUTO_INHERITED |
                                     SE_SACL_AUTO_INHERITED)))
                        {
                            //
                            // Before we convert this, we may need to reread the SD... if
                            // we don't have owner and group
                            //
                            if(!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION)  ||
                               !FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION))
                            {
                                AccFree(pChildSD);
                                pChildSD = NULL;
                                dwErr = ReadRegistrySecurityInfo(
                                                    hChild,
                                                    SeInfo                          |
                                                        OWNER_SECURITY_INFORMATION  |
                                                        GROUP_SECURITY_INFORMATION,
                                                    (PSECURITY_DESCRIPTOR *)&pChildSD);
                                if(dwErr == ERROR_ACCESS_DENIED)
                                {
                                    RegCloseKey(hChild);
                                    dwErr = RegOpenKeyEx(hParent,
                                                         wszBuff,
                                                         0,
                                                         RegGetDesiredAccess(MODIFY_ACCESS_RIGHTS,
                                                              SeInfo    |
                                                              OWNER_SECURITY_INFORMATION |
                                                              GROUP_SECURITY_INFORMATION)   |
                                                         KEY_ENUMERATE_SUB_KEYS             |
                                                         KEY_QUERY_VALUE,
                                                         &hChild);
                                    if(dwErr == ERROR_SUCCESS)
                                    {
                                        dwErr = ReadRegistrySecurityInfo(
                                                            hChild,
                                                            SeInfo                          |
                                                                OWNER_SECURITY_INFORMATION  |
                                                                GROUP_SECURITY_INFORMATION,
                                                            (PSECURITY_DESCRIPTOR *)&pChildSD);
                                    }
                                }
                            }

                            if(dwErr == ERROR_SUCCESS)
                            {
                                dwErr = ConvertToAutoInheritSD(pOldParentSD,
                                                               pChildSD,
                                                               TRUE,
                                                               &gRegGenMapping,
                                                               &pNewSD);
                                AccFree(pChildSD);

                                if(dwErr == ERROR_SUCCESS)
                                {
                                    pChildSD = (SECURITY_DESCRIPTOR *)pNewSD;
                                    fAccFreeChild = FALSE;
                                    pNewSD = NULL;
                                }
                            }
                        }


                        //
                        // Now, compute the new security descriptor
                        if(dwErr == ERROR_SUCCESS)
                        {
                            DebugDumpSD("CPOS ParentSD", pParentSD);
                            DebugDumpSD("CPOS CreatorSD",  pChildSD);

                            if(CreatePrivateObjectSecurityEx(pParentSD,
                                                             pChildSD,
                                                             &pNewSD,
                                                             NULL,
                                                             TRUE,
                                                             SEF_DACL_AUTO_INHERIT      |
                                                                 SEF_SACL_AUTO_INHERIT  |
                                                                 SEF_AVOID_OWNER_CHECK  |
                                                                 SEF_AVOID_PRIVILEGE_CHECK,
                                                             hProcessToken,
                                                             &gRegGenMapping) == FALSE)
                            {
                                dwErr = GetLastError();
                            }
                        }
#ifdef DBG
                        else
                        {
                            DebugDumpSD("CPOS NewChild", pNewSD);
                        }
#endif

                        if(dwErr == ERROR_SUCCESS)
                        {
                            //
                            // If the resultant child is protected, don't bother propagating
                            // down.
                            //
                            if(FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION))
                            {
                                if(DACL_PROTECTED(pNewSD))
                                {
                                    fProtectedFlag |= SE_DACL_PROTECTED;
                                }
                            }

                            if(FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION))
                            {
                                if(SACL_PROTECTED(pNewSD))
                                {
                                    fProtectedFlag |= SE_SACL_PROTECTED;
                                }
                            }

                            if(FLAG_ON( fProtectedFlag, (SE_DACL_PROTECTED | SE_SACL_PROTECTED)))
                            {
                                cSubKeys = 0;
                                dwErr = InsertPropagationFailureEntry(LogList,
                                                                      0,
                                                                      fProtectedFlag,
                                                                      wszBuff);
                            }

                            //
                            // If we haven't changed the acl, security descriptor, then
                            // we can also quit
                            //
                            if(EqualSecurityDescriptors(pNewSD, pChildSD))
                            {
                                cSubKeys = 0;
                            }
                        }


                    }

                    //
                    // Now, if it's a directory, call ourselves
                    //
                    if(dwErr == ERROR_SUCCESS && cSubKeys != 0)
                    {
                        dwErr = PropagateRegRightsDeep(pChildSD,
                                                       pNewSD,
                                                       SeInfo,
                                                       hChild,
                                                       pcProcessed,
                                                       pfStopFlag,
                                                       fProtectedFlag,
                                                       hProcessToken,
                                                       LogList);

                        if(dwErr == ERROR_ACCESS_DENIED)
                        {
                            dwErr = InsertPropagationFailureEntry(LogList,
                                                                  dwErr,
                                                                  0,
                                                                  wszBuff);
                        }
                    }

                    //
                    // Free the old child, since we won't need it anymore
                    //
                    if(fAccFreeChild == TRUE)
                    {
                        AccFree(pChildSD);
                    }
                    else
                    {
                        DestroyPrivateObjectSecurity((PSECURITY_DESCRIPTOR *)
                                                                   &pChildSD);
                    }
                    pChildSD = NULL;

                }
            }

            acDebugOut((DEB_TRACE_PROP,
                        "Processed %ws: %lu\n",
                        wszBuff,
                        dwErr));

            //
            // Finally, set the new security
            //
            if(dwErr == ERROR_SUCCESS)
            {
                //
                // Now, we'll simply stamp it on the object
                //

                dwErr = SetRegistrySecurityInfo(hChild,
                                                SeInfo,
                                                NULL,
                                                pNewSD);
                (*pcProcessed)++;

            }


            DestroyPrivateObjectSecurity(&pNewSD);
            pNewSD = NULL;

            CLEANUP_ON_INTERRUPT(pfStopFlag);
            iIndex++;
        }
    }

    if(dwErr == ERROR_NO_MORE_FILES)
    {
        dwErr = ERROR_SUCCESS;
    }

RegCleanup:
    if(hChild != NULL)
    {
        RegCloseKey(hChild);
    }

    if(pNewSD != NULL)
    {
        DestroyPrivateObjectSecurity(&pNewSD);
    }

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




//+---------------------------------------------------------------------------
//
//  Function:   SetAndPropRegRights
//
//  Synopsis:   Sets the access on the given registry path and propagates
//              it as necessary
//
//  Arguments:  [IN  hReg]              --      Handle to the reg. object to set
//              [IN  pwszPath]          --      Registry path referred to by hReg,
//                                              if known
//              [IN  SeInfo]            --      Security information to set
//              [IN  pParentSD]         --      Security descriptor of the parent
//              [IN  pSD]               --      SD to set
//              [IN  pfStopFlag]        --      Address of the stop flag
//                                              to be monitored
//              [IN  pcProcessed]       --      count of processed items to
//                                              be incremented.
//
//  Returns:    ERROR_SUCCESS           --      Success
//
//----------------------------------------------------------------------------
DWORD
SetAndPropRegRights(IN  HKEY                    hReg,
                    IN  PWSTR                   pwszPath,
                    IN  SECURITY_INFORMATION    SeInfo,
                    IN  PSECURITY_DESCRIPTOR    pParentSD,
                    IN  PSECURITY_DESCRIPTOR    pSD,
                    IN  PULONG                  pfStopFlag,
                    IN  PULONG                  pcProcessed)
{
    acDebugOut((DEB_TRACE, "in SetAndPropRegRights\n"));

    DWORD                   dwErr = ERROR_SUCCESS;
    PSECURITY_DESCRIPTOR    pOldObjSD = NULL;
    PSECURITY_DESCRIPTOR    pUpdatedSD = NULL;
    PSECURITY_DESCRIPTOR    pVerifySD = NULL;
    BOOL                    fManualProp = FALSE;
    ULONG                   fProtected = 0;
    ULONG                   cSubKeys;
    HANDLE                  hProcessToken = NULL;
    PSID                    pOwner = NULL, pGroup = NULL;

    CSList                  FailureLogList(FreePropagationFailureListEntry);



    //
    // Ok, read the existing security
    //
    dwErr = ReadRegistrySecurityInfo(hReg,
                                     SeInfo,
                                     &pOldObjSD);

    //
    // Now, we'll write out the current, and then read it back and make sure
    // that it's properly updated
    //
    if(dwErr == ERROR_SUCCESS &&
        FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION) )
    {
        CLEANUP_ON_INTERRUPT(pfStopFlag);
        dwErr = SetRegistrySecurityInfo(hReg,
                                        SeInfo,
                                        NULL,
                                        pSD);
        if(dwErr == ERROR_SUCCESS)
        {
            (*pcProcessed)++;
            CLEANUP_ON_INTERRUPT(pfStopFlag);

            dwErr = ReadRegistrySecurityInfo(hReg,
                                             SeInfo,
                                             &pVerifySD);
            if(dwErr == ERROR_SUCCESS)
            {
                //
                // Check to see if this was done uplevel...
                //
                PISECURITY_DESCRIPTOR pISD = (PISECURITY_DESCRIPTOR)pVerifySD;
                if(!(FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION) &&
                    FLAG_ON(pISD->Control, SE_DACL_AUTO_INHERITED)) &&
                   !(FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION) &&
                    FLAG_ON(pISD->Control, SE_SACL_AUTO_INHERITED)))
                {
                    //
                    // It's not uplevel, so we'll turn the AutoInherit
                    // flags on, rewrite it, and do our own propagation,
                    // only if this is a container and we're setting the
                    // dacl or sacl
                    //
                    if(FLAG_ON(SeInfo,
                               (DACL_SECURITY_INFORMATION |
                                            SACL_SECURITY_INFORMATION)))
                    {
                        fManualProp = TRUE;
                    }

                    if(FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION))
                    {
                        pISD->Control |= SE_DACL_AUTO_INHERITED;
                    }

                    if(FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION))
                    {
                        pISD->Control |= SE_SACL_AUTO_INHERITED;
                    }

                    //
                    // Go ahead and upgrade it to autoinherit
                    //
                    if(!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION)  ||
                       !FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION))
                    {
                        //
                        // Need to reread it to get the owner and group
                        //
                        AccFree(pVerifySD);
                        dwErr = ReadRegistrySecurityInfo(hReg,
                                                         SeInfo |
                                                            OWNER_SECURITY_INFORMATION |
                                                            GROUP_SECURITY_INFORMATION,
                                                         &pVerifySD);
                        //
                        // If we failed to read it because we didn't originally have permissions
                        // and we have the path, we'll try to reopen the handle with the
                        // proper rights
                        //
                        if(dwErr == ERROR_ACCESS_DENIED && pwszPath != NULL)
                        {
                            HKEY    hReg2;

                            dwErr = OpenRegistryObject(pwszPath,
                                                       RegGetDesiredAccess(READ_ACCESS_RIGHTS,
                                                            SeInfo |
                                                                OWNER_SECURITY_INFORMATION |
                                                                GROUP_SECURITY_INFORMATION),
                                                       (PHANDLE)&hReg2);

                            if(dwErr == ERROR_SUCCESS)
                            {
                                dwErr = ReadRegistrySecurityInfo(hReg2,
                                                         SeInfo |
                                                            OWNER_SECURITY_INFORMATION |
                                                            GROUP_SECURITY_INFORMATION,
                                                         &pVerifySD);
                                RegCloseKey(hReg2);
                            }

                        }


                        //
                        // Set our owner/group in the old security descriptor
                        //
                        if(dwErr == ERROR_SUCCESS)
                        {
                            BOOL    fDefaulted;

                            if(!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION))
                            {
                                if(!GetSecurityDescriptorOwner(pVerifySD, &pOwner, &fDefaulted))
                                {
                                    dwErr = GetLastError();
                                }
                            }

                            if(!FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION))
                            {
                                if(!GetSecurityDescriptorGroup(pVerifySD, &pGroup, &fDefaulted))
                                {
                                    dwErr = GetLastError();
                                }
                            }

                            if(dwErr == ERROR_SUCCESS)
                            {
                                //
                                // If it's self relative, we'll have to make it absolute.
                                //
                                if(FLAG_ON(((SECURITY_DESCRIPTOR *)pSD)->Control,
                                            SE_SELF_RELATIVE))
                                {
                                    PSECURITY_DESCRIPTOR pSD2;
                                    dwErr = MakeSDAbsolute(pSD,
                                                           SeInfo,
                                                           &pSD2,
                                                           pOwner,
                                                           pGroup);
                                    if(dwErr == ERROR_SUCCESS)
                                    {
                                        AccFree(pSD);
                                        pSD = pSD2;
                                    }
                                }
                                else
                                {
                                    if(pOwner != NULL)
                                    {
                                        if(SetSecurityDescriptorOwner(pOldObjSD,
                                                                      pOwner,
                                                                      FALSE) == FALSE)
                                        {
                                            dwErr = GetLastError();
                                        }
                                    }

                                    if(pGroup != NULL)
                                    {
                                        if(SetSecurityDescriptorGroup(pOldObjSD,
                                                                      pGroup,
                                                                      FALSE) == FALSE)
                                        {
                                            dwErr = GetLastError();
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if(dwErr == ERROR_SUCCESS)
                    {
                        dwErr = GetCurrentToken( &hProcessToken );
                    }


                    if(dwErr == ERROR_SUCCESS)
                    {
                        dwErr = ConvertToAutoInheritSD(pParentSD,
                                                       pOldObjSD,
                                                       TRUE,
                                                       &gRegGenMapping,
                                                       &pUpdatedSD);
                        if(dwErr == ERROR_SUCCESS)
                        {
                            //
                            // Now, if we're going to do manual propagation,
                            // we'll write out the old SD until we get everyone
                            // else updated
                            //
                            PSECURITY_DESCRIPTOR    pWriteSD = pUpdatedSD;
                            if(fManualProp == TRUE)
                            {
                                pWriteSD = pOldObjSD;
                            }
                            else
                            {
                                if(SetPrivateObjectSecurity(SeInfo,
                                                            pParentSD,
                                                            &pUpdatedSD,
                                                            &gRegGenMapping,
                                                            hProcessToken) == FALSE)
                                {
                                    dwErr = GetLastError();
                                }
                            }

                            //
                            // Reset it...
                            //
                            if(dwErr == ERROR_SUCCESS)
                            {
                                dwErr = SetRegistrySecurityInfo(hReg,
                                                                SeInfo,
                                                                NULL,
                                                                pWriteSD);
                            }
                        }
                    }
                    else
                    {
                        pVerifySD = NULL;
                    }



                }

            }

        }
    }
    else
    {
        if(dwErr == ERROR_SUCCESS &&
           FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION))
        {

            dwErr = SetRegistrySecurityInfo(hReg,
                                            SeInfo,
                                            NULL,
                                            pSD);
        }
    }

    //
    // Ok, now if we're doing propagation, we'll get busy and do that...
    //
    if(dwErr == ERROR_SUCCESS && fManualProp == TRUE)
    {
        //
        // Set our protected flags.  If we aren't messing with a particular acl, we'll
        // pretend it's protected
        //

        fProtected = ((SECURITY_DESCRIPTOR *)pUpdatedSD)->Control &
                                                ~(SE_DACL_PROTECTED | SE_SACL_PROTECTED);
        if(FLAG_ON(fProtected, SE_DACL_PROTECTED ) || FLAG_ON(fProtected, SE_SACL_PROTECTED ))
        {
            dwErr = InsertPropagationFailureEntry(FailureLogList,
                                                  0,
                                                  fProtected,
                                                  pwszPath == NULL ?
                                                        L"<Unkown Registry Root>" :
                                                        pwszPath);
        }

        if(!FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION))
        {
            fProtected |= SE_DACL_PROTECTED;
        }

        if(!FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION))
        {
            fProtected |= SE_SACL_PROTECTED;
        }

        //
        // Ok, go ahead and do deep.  This will possibly save us
        // some storage space in the long run...
        //
        dwErr = PropagateRegRightsDeep(pOldObjSD,
                                       pUpdatedSD,
                                       SeInfo,
                                       hReg,
                                       pcProcessed,
                                       pfStopFlag,
                                       fProtected,
                                       hProcessToken,
                                       FailureLogList);
        if(dwErr == ERROR_ACCESS_DENIED)
        {
            dwErr = InsertPropagationFailureEntry(FailureLogList,
                                                  dwErr,
                                                  0,
                                                  pwszPath == NULL ?
                                                        L"<Unkown Registry Root>" :
                                                        pwszPath);
        }


        //
        // If that worked, write out our updated root security descriptor
        //
        if(dwErr == ERROR_SUCCESS)
        {
            PSECURITY_DESCRIPTOR    pSet;

            if(!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION))
            {
                if(!SetSecurityDescriptorOwner(pSD, pOwner, FALSE))
                {
                    dwErr = GetLastError();
                }
            }

            if(!FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION))
            {
                if(!SetSecurityDescriptorGroup(pSD, pGroup, FALSE))
                {
                    dwErr = GetLastError();
                }
            }

            if(dwErr == ERROR_SUCCESS)
            {
                if(CreatePrivateObjectSecurityEx(pParentSD,
                                                 pSD,
                                                 &pSet,
                                                 NULL,
                                                 TRUE,
                                                 SEF_DACL_AUTO_INHERIT      |
                                                     SEF_SACL_AUTO_INHERIT  |
                                                     SEF_AVOID_OWNER_CHECK  |
                                                     SEF_AVOID_PRIVILEGE_CHECK,
                                                 hProcessToken,
                                                 &gRegGenMapping) == FALSE)
                {
                    dwErr = GetLastError();
                }
                else
                {
                    dwErr = SetRegistrySecurityInfo(hReg,
                                                    SeInfo,
                                                    NULL,
                                                    pSet);
                    DestroyPrivateObjectSecurity(&pSet);
                }
            }
        }
    }


    if(dwErr == ERROR_SUCCESS)
    {
        dwErr = WritePropagationFailureList(MARTAEVT_REGISTRY_PROPAGATION_FAILED,
                                            FailureLogList,
                                            hProcessToken);
    }

RegCleanup:
    AccFree(pOldObjSD);
    AccFree(pVerifySD);

    if(pUpdatedSD != NULL)
    {
        DestroyPrivateObjectSecurity(&pUpdatedSD);
    }

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




//+---------------------------------------------------------------------------
//
//  Function:   ConvertRegHandleToName
//
//  Synopsis:   Determines the registry path for a handle.  Issues an
//              NtQueryInformationFile to determine the path name
//
//  Arguments:  [IN  hKey]              --      The (open) handle of the file
//                                              object
//              [OUT ppwszName]         --      Where the name is returned
//
//  Returns:    ERROR_SUCCESS           --      Succcess
//              ERROR_NOT_ENOUGH_MEMORY --      A memory allocation failed
//
//  Notes:      The returned memory must be freed with AccFree
//
//----------------------------------------------------------------------------
DWORD
ConvertRegHandleToName(IN  HKEY       hKey,
                       OUT PWSTR      *ppwszName)
{
    DWORD   dwErr = ERROR_SUCCESS;

    //
    // First, determine the size of the buffer we need...
    //
    BYTE        pBuff[512];
    ULONG       cLen = 0;
    POBJECT_NAME_INFORMATION pNI = NULL;
    PWSTR       pwszPath = NULL;
    NTSTATUS    Status = NtQueryObject(hKey,
                                       ObjectNameInformation,
                                       (POBJECT_NAME_INFORMATION)pBuff,
                                       512,
                                       &cLen);
    if(!NT_SUCCESS(Status))
    {
        if(Status == STATUS_BUFFER_TOO_SMALL ||
            Status == STATUS_INFO_LENGTH_MISMATCH)
        {
            //
            // Fine.. Allocate a big enough buffer
            //
            pNI = (POBJECT_NAME_INFORMATION)AccAlloc(cLen);
            if(pNI != NULL)
            {
                Status = NtQueryObject(hKey,
                                       ObjectNameInformation,
                                       pNI,
                                       cLen,
                                       NULL);
                if(NT_SUCCESS(Status))
                {
                    pwszPath = pNI->Name.Buffer;
                }
                AccFree(pNI);
            }
            else
            {
                Status = STATUS_NO_MEMORY;
            }
        }

        if(dwErr == ERROR_SUCCESS)
        {
            dwErr = RtlNtStatusToDosError(Status);
        }

    }
    else
    {
        pwszPath = ((POBJECT_NAME_INFORMATION)pBuff)->Name.Buffer;
    }

    //
    // If we have a path, then it's a simple matter to pull out the appropriate string,
    // since what gets returned is something in the form of \\REGISTRY\\MACHINE\\somepath
    // which is pretty close to what we want
    //
    #define REG_OBJ_TAG L"\\REGISTRY\\"

    if(pwszPath != NULL)
    {
        pwszPath += (sizeof(REG_OBJ_TAG) / sizeof(WCHAR) - 1);
        ACC_ALLOC_AND_COPY_STRINGW(pwszPath,
                                   *ppwszName,
                                   dwErr);

    }

    return(dwErr);
}