2011-03-21

Programatically setting and applying Local Group Policies on Windows

One of the most annoying aspects of device driver installation on Windows, and one of the main gripe people are expected to have with the current libwdi (which has since been fixed programmatically using the technique exposed below), is the Windows default of creating a system restore point each time it installs a new driver. Having seen restore point creations that take more than 5 minutes, and ending up in failure as a result, since 5 minutes is the default timeout for a point creation, I have been looking at ways to change this default behaviour.

Enters the Local Group Policy. If you're lucky enough to run a version of Windows that enables it, you might be aware of the gpedit mmc tool (launch the command gpedit.msc to access it), which can be used to alter various Windows settings, a.k.a. Group Policies. In general, Group Policies are settings that are applied by Active Directory administrators in a networked environment, but the non networked part, called Local Group Policy can also be used by the end users to control the behaviour of their local machine or user account, should the network administrator decide not to override those. Of special interest to us is the Administrative Templates → System → Device Installation → "Prevent creation of a system restore point during device activity..." setting, which you can access after launching gpedit.msc. It works, and is exactly what we have been looking for.

But then again, we don't want to rely on end users to change this setting themselves, especially if they are running the Basic or Home edition of Windows 7 or Vista where gpedit is not available. We want to do it programmatically.

If you use the RegFormApp tool from NirSoft, to monitor what gpedit actually does when enabling this setting, you find that it simply creates a new DisableSystemRestore DWORD in the registry at:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Group Policy Objects\{<some-guid>}Machine\Software\Policies\Microsoft\Windows\DeviceInstall\Settings

Brilliant. The much decried Registry might actually prove its worth, if all you have to do to toggle system restore point creation on driver installation, is modify a key there... except, we're talking about a Microsoft way of implementing a solution, and it would be way too simple if Windows' own Local Group Policy settings were stored in the registry. The key created by gpedit there is in fact useless for the programmer, because it's just a some temporary storage for gpedit and not at all where the system looks when deciding whether it should create a restore point. As a matter of fact, the GUID changes every time you run gpedit.

So now we need to figure out a way to set our Local Group Policy programmatically, as gpedit does, and you can trust that it's not going to be as simple as what we were hoping for. Thankfully, if you look hard enough, you may end up on the Microsoft's USGCB Tech Blog page dealing with Utilities for automating Local Group Policy management. These tools are precisely the kind of sample we are looking for, as the ImportRegPol there can pick up a .pol file and apply the LPGO associated with it, and they come with the source.

To cut a long story short, you will need to use an IGroupPolicyObject Interface object, and below is the C source (with no error checking whatsoever) that one can use to disable the creation of a restore point during driver installation on Windows Vista and Windows 7:
#include <gpedit.h>

DWORD val, val_size=sizeof(DWORD);
HRESULT hr;
IGroupPolicyObject* pLGPO;
HKEY machine_key, dsrkey;
// MSVC is finicky about these ones => redefine them
const IID my_IID_IGroupPolicyObject = 
 { 0xea502723, 0xa23d, 0x11d1, {0xa7, 0xd3, 0x0, 0x0, 0xf8, 0x75, 0x71, 0xe3} };
const IID my_CLSID_GroupPolicyObject = 
 { 0xea502722, 0xa23d, 0x11d1, {0xa7, 0xd3, 0x0, 0x0, 0xf8, 0x75, 0x71, 0xe3} };
GUID ext_guid = REGISTRY_EXTENSION_GUID;
// This next one can be any GUID you want
GUID snap_guid = { 0x3d271cfc, 0x2bc6, 0x4ac2, {0xb6, 0x33, 0x3b, 0xdf, 0xf5, 0xbd, 0xab, 0x2a} };

// Create an instance of the IGroupPolicyObject class
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CoCreateInstance(&my_CLSID_GroupPolicyObject, NULL, CLSCTX_INPROC_SERVER,
 &my_IID_IGroupPolicyObject, (LPVOID*)&pLGPO);

// We need the machine LGPO (if C++, no need to go through the lpVtbl table)
pLGPO->lpVtbl->OpenLocalMachineGPO(pLGPO, GPO_OPEN_LOAD_REGISTRY);
pLGPO->lpVtbl->GetRegistryKey(pLGPO, GPO_SECTION_MACHINE, &machine_key);

// The disable System Restore is a DWORD value of Policies\Microsoft\Windows\DeviceInstall\Settings
RegCreateKeyEx(machine_key, "Software\\Policies\\Microsoft\\Windows\\DeviceInstall\\Settings",
 0, NULL, 0, KEY_SET_VALUE | KEY_QUERY_VALUE, NULL, &dsrkey, NULL);
 
// Create the value
val = 1;
RegSetKeyValue(dsrkey, NULL, "DisableSystemRestore", REG_DWORD, &val, sizeof(val));
RegCloseKey(dsrkey);

// Apply policy and free resources
pLGPO->lpVtbl->Save(pLGPO, TRUE, TRUE, &ext_guid, &snap_guid);
RegCloseKey(machine_key);
pLGPO->lpVtbl->Release(pLGPO);
Disabling of system restore during driver installation - coming soon to a libwdi powered application near you!

5 comments:

  1. I understood your code and it works fine with "SOFTWARE\Microsoft\Windows\Current Version" but, I am unable to open the "SOFTWARE\Microsoft\Windows NT\CurrentVersion\SeCEdit\Reg Values key. Could you please tell me how can I open that key?

    ReplyDelete
  2. I am working on implementing user based software restriction policy programmatically for local group policy object. If I create a policy through Domain Controller ,I do have option for software restriction policy in user configuration but in local group policy editor I don't have option for that. When I look for the changes made by policy applied from Domain Controller in registry, they modify registry values for specific users on path HKEY_USERS(SID of User)\Softwares\Policies\Microsoft\Windows\Safer\Codeidentifiers They also have registry.pol stored in SYSvol folder in Domain Controller. When I make the same changes in registry to block any other application, application is getting blocked. I achieved what I wanted but is it right to modify registry values ?

    If the implementation ever changes, if Windows notices that there are group policy settings in place that aren't in the actual group policy, will they remove it ?

    ReplyDelete
  3. The "Save" method of IGroupPolicyObject interface is failing with an error code 32 (decimal). what could be causing this issue?

    ReplyDelete
  4. I have to modify my local group policy that is "Configure automatic update" and "specify intranet Microsoft update service" programatically. The above approach is not working for me. Any suggestion most welcome..

    ReplyDelete
  5. Hi Nilesh,

    are you able to modify "Configure Automatic update" ?

    Could you please share the solution if possible.

    ReplyDelete