Search code examples
c++windowsregistryaccess-deniedaccess-rights

How do I read/change the registry values that another windows service created?


So basically I have a service which is meant to act as my Program Update Manager. This is all in my efforts to make an automatically updating program that doesn't need the user to be signed in.

So my update manager when installed creates some initial registry values/structures with the following code:

LPCWSTR strInITKeyName = L"SOFTWARE\\InIT\\";
DWORD rtime = 0;
HKEY InITKey;
LONG nInITError;
nInITError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, strInITKeyName, 0, 0, &InITKey);
if (ERROR_NO_MATCH == nInITError || ERROR_FILE_NOT_FOUND == nInITError)
{
    std::cout << "Registry key not found.  Setting up..." << std::endl;
    long nError = RegCreateKeyEx(HKEY_LOCAL_MACHINE, strInITKeyName, 0L, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &InITKey, NULL);
    if (ERROR_SUCCESS != nError)
        std::cout << "Error:  Could not create registry key HKEY_LOCAL_MACHINE\\" << strInITKeyName << std::endl << "\tERROR: " << nError << std::endl;
    else
    {
        std::cout << "Successfully created registry key HKEY_LOCAL_MACHINE\\" << strInITKeyName << std::endl;

        // See https://www.experts-exchange.com/questions/10171094/Using-RegSetKeySecurity.html for example
        //SECURITY_DESCRIPTOR sd;
        //PACL pDacl = NULL;

        //RegSetKeySecurity(InITKey);
    }
}
else if (nInITError == ERROR_ACCESS_DENIED)
{
    long nError = RegCreateKeyEx(HKEY_LOCAL_MACHINE, strInITKeyName, 0L, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &InITKey, NULL);
    if (ERROR_SUCCESS != nError)
        std::cout << "Error:  Could not create registry key HKEY_LOCAL_MACHINE\\" << strInITKeyName << std::endl << "\tERROR: " << nError << std::endl;
    else
        std::cout << "Successfully created registry key HKEY_LOCAL_MACHINE\\" << strInITKeyName << std::endl;
}
else if (ERROR_SUCCESS != nInITError)
{
    std::cout << "Cannot open registry key HKEY_LOCAL_MACHINE\\" << strInITKeyName << std::endl << "\tERROR: " << nInITError << std::endl;
    rtime = 0;
}

// Generate guid
//
GUID guid;
HRESULT hr = CoCreateGuid(&guid);

// Convert the GUID to a string
OLECHAR* guidString;
StringFromCLSID(guid, &guidString);

// Setup registry values
// Sets clientguid value and ties to strInITKeyName
std::wstring clientguid = guidString;   // InITKey
clientguid = clientguid.substr(1, 36);

LONG nClientGUIDError = RegSetValueEx(InITKey, L"clientguid", NULL, REG_SZ, (const BYTE*)clientguid.c_str(), (clientguid.size() + 1) * sizeof(wchar_t));
if (nClientGUIDError)
    std::cout << "Error: " << nClientGUIDError << " Could not set registry value: " << "clientguid" << std::endl;
else
    std::wcout << "Successfully set InIT clientguid to " << clientguid << std::endl;

// ensure memory is freed
::CoTaskMemFree(guidString);
RegCloseKey(InITKey);

When it is uninstalled, it then deletes the registry values. My actual program is a windows service that will be running on the installed computer which needs to access some of those registry values.

Previously I did not have this update manager, and instead was setting the values in the registry in the actual program service. Reading, and writing worked just fine then. But since I've switched to having my Update Manager set these initial values and intend for my main windows service to access them.

When I try to do this, I've without fail, every time, received an ERROR_ACCESS_DENIED error, despite trying all kinds of different Security tokens like KEY_READ || KEY_WOW64_64KEY and others in many combinations when trying to open the key. As you saw above, I am or-ing KEY_ALL_ACCESS when setting the token. I would think I should be able to access it fine, but it doesn't let me. The only conclusion I can come to is that somehow my Update manager has ownership over the keys/values in the registry.

What would the right code be to access these registry files from my main windows service?

My current code for accessing those registry values (the clientguid that I generate on install of the Update Manager see above code):

// Log a service start message to the Application log.
WriteEventLogEntry(L"InITService Starting in OnStart", EVENTLOG_INFORMATION_TYPE);
this->m_ServiceLogger->info("Initialized logger bruh");

// Query clientguid from registry
HKEY InITKey;
std::wstring valuename;

ULONG nError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\InIT\\", 0, KEY_READ || KEY_WOW64_64KEY, &InITKey);

DWORD dwBufferSize = TOTALBYTES;
DWORD cbData;

WCHAR *wcBuffer = (WCHAR*)malloc(dwBufferSize);
cbData = dwBufferSize;
if (nError == ERROR_SUCCESS)
    this->m_ServiceLogger->info("Getting reg");

if (nError == ERROR_SUCCESS)
{
    std::wstring clientguid;
    clientguid = L"";
    valuename = L"clientguid";

    nError = RegQueryValueExW(HKEY_LOCAL_MACHINE, valuename.c_str(), 0, NULL, (LPBYTE) wcBuffer, &cbData);

    while (nError == ERROR_MORE_DATA)       // Get a buffer that is big enough if not already
    {
        this->m_ServiceLogger->info("Increasing clientguid buffer size");
        dwBufferSize += BYTEINCREMENT;
        wcBuffer = (WCHAR*) realloc( wcBuffer, dwBufferSize );
        cbData = dwBufferSize;

        nError = RegQueryValueExW(HKEY_LOCAL_MACHINE, valuename.c_str(), 0, NULL, (LPBYTE)wcBuffer, &cbData);
    }
    if (ERROR_SUCCESS == nError)
    {
        clientguid = wcBuffer;
        std::string cg(clientguid.begin(), clientguid.end());
        this->m_ClientGuid = cg;
        this->m_ServiceLogger->info("Clientguid yo: " + cg);
    }
    else if (nError = ERROR_ACCESS_DENIED)
        this->m_ServiceLogger->info("ClientGUID:  Access Denied");
    if (!this->checkRegistryValues())
    {
        this->generateRegistry();
    }
}
else
{
    std::stringstream errstr;
    errstr << nError;
    this->m_ServiceLogger->info("Error: " + errstr.str() + " RegOpenKeyEx failed");
}

this->setSchedulingUtility();  // Hardcoded to set scheduled update at 1:00 AM

WriteEventLogEntry(L"InITService Initialized Schedule in OnStart", EVENTLOG_INFORMATION_TYPE);
this->m_ServiceLogger->info("Initialized ClientGUID: " + this->m_ClientGuid);
this->m_ServiceLogger->info("Initialized CurrentVersion: " + this->m_CurrentVersion);
this->m_ServiceLogger->info("Initialized WebServerURL: " + this->m_POSTAddress);
this->m_ServiceLogger->flush();

RegCloseKey(InITKey);

// Queue the main service function for execution in a worker thread.
CThreadPool::QueueUserWorkItem(&CSampleService::ServiceWorkerThread, this);

Solution

  • When calling RegOpenKeyEx(), you need to use the BITWISE OR (|) operator instead of the LOGICAL OR (||) operator. Change:

    KEY_READ || KEY_WOW64_64KEY
    

    To:

    KEY_READ | KEY_WOW64_64KEY
    

    However, when your Update Manager calls RegOpenKeyEx(), it is not specifying any access rights at all, it is setting the samDesired parameter to 0. It should be setting it to at least KEY_SET_VALUE instead. When it calls RegCreateKeyEx() if RegOpenKeyEx() fails, it is setting the samDesired to KEY_ALL_ACCESS. Don't do that. Use only the access rights that you actually need (KEY_SET_VALUE, etc).

    In any case, there is no need to call both RegOpenKeyEx() and RegCreateKeyEx() anyway. Just call RegCreateKeyEx() by itself. It will open an existing key, and create a non-existing key. Its dwDisposition output parameter will tell you which happened.

    There are other mistakes in this code, too. Like passing the wrong HKEY to RegQueryValueEx(), and not checking for the ERROR_ACCESS_DENIED error code correctly (using the = assignment operator instead of the == comparison operator). And memory leaks.

    Try something more like this instead:

    Update Manager:

    LPCWSTR strInITKeyName = L"SOFTWARE\\InIT\\";
    HKEY InITKey;
    DWORD dwDisposition;
    
    LONG nError = RegCreateKeyEx(HKEY_LOCAL_MACHINE, strInITKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE | KEY_WOW64_64KEY, NULL, &InITKey, &dwDisposition);
    
    if (ERROR_SUCCESS != nError)
    {
        std::cout << "Error: Could not open/create registry key HKEY_LOCAL_MACHINE\\" << strInITKeyName << std::endl << "\tERROR: " << nError << std::endl;
    }
    else
    {
        std::cout << "Successfully " << ((REG_CREATED_NEW_KEY == dwDisposition) ? "created" : "opened") << " registry key HKEY_LOCAL_MACHINE\\" << strInITKeyName << std::endl;
    
        // Generate guid and convert to a string
        //
        std::wstring clientguid;
        GUID guid;
    
        HRESULT hr = CoCreateGuid(&guid);
        if (FAILED(hr))
        {
            std::cout << "Error: Could not generate clientguid" << std::endl << "\tERROR: " << (int)hr << std::endl;
        }
        else
        {
            WCHAR guidString[40] = {0};
            int len = StringFromGUID2(guid, guidString, 40);
            if (len > 2)
            {
                // Sets clientguid value and ties to strInITKeyName
                clientguid.assign(&guidString[1], len-2);
            }
        }
    
        // Setup registry values
    
        nError = RegSetValueEx(InITKey, L"clientguid", NULL, REG_SZ, (const BYTE*) clientguid.c_str(), (clientguid.size() + 1) * sizeof(wchar_t));
        if (ERROR_SUCCESS != nError)
            std::cout << "Error: Could not set registry value: clientguid" << std::endl << "\tERROR: " << nError << std::endl;
        else
            std::wcout << "Successfully set InIT clientguid to " << clientguid << std::endl;
    
        RegCloseKey(InITKey);
    }
    

    Program Service:

    ...
    
    // Query clientguid from registry
    HKEY InITKey;
    
    LONG nError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\InIT\\", 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &InITKey);
    
    if (ERROR_SUCCESS != nError)
    {
        std::stringstream errstr;
        errstr << nError;
        this->m_ServiceLogger->info("Error: " + errstr.str() + " RegOpenKeyEx failed");
    }
    else
    {
        this->m_ServiceLogger->info("Getting reg");
    
        std::vector<BYTE> buffer(TOTALBYTES + sizeof(wchar_t), 0); // extra room for a null terminator, in case it is missing in the Registry data
        DWORD cbData = TOTALBYTES;
    
        do
        {
            nError = RegQueryValueExW(InITKey, L"clientguid", 0, NULL, &buffer[0], &cbData);
    
            if (ERROR_MORE_DATA != nError)
                break;
    
            // Get a buffer that is big enough if not already
            this->m_ServiceLogger->info("Resizing clientguid buffer");
    
            buffer.resize(cbData + sizeof(wchar_t));
        }
        while (true);
    
        if (ERROR_SUCCESS == nError)
        {
            std::wstring clientguid = (WCHAR*) &buffer[0];
            std::string cg(clientguid.begin(), clientguid.end());
    
            this->m_ClientGuid = cg;
            this->m_ServiceLogger->info("Clientguid yo: " + cg);
        }
        else if (ERROR_ACCESS_DENIED == nError)
        {
            this->m_ServiceLogger->info("ClientGUID: Access Denied");
        }
        else
        {
            std::stringstream errstr;
            errstr << nError;
            this->m_ServiceLogger->info("Error: " + errstr.str() + " RegQueryValueEx failed");
        }
    
        RegCloseKey(InITKey);
    
        if (!this->checkRegistryValues())
        {
            this->generateRegistry();
        }
    }
    
    ...