What API can I use to get the "Key Usage" from a certificate. I Specifically want to know if a given certificate has "Digital Signature" or not.
Below screenshot is the detail of a certificate in windows. I need the API that gives me the "Key Usage".
The code is for windows and I am writing my code in C++.
Thank you
Sam
Start with CertOpenStore, then call CertFindCertificateInStore in a loop until you find the certificate you are interested in.
The returned CERT_CONTEXT contains a pointer to a CERT_INFO struct. You will then want to walk the rgExtension member which is an array of CERT_EXTENSION objects. The one you care about has pszObjId set to szOID_KEY_USAGE_RESTRICTION, which will then give you this data: CERT_KEY_USAGE_RESTRICTION_INFO where the RestrictedKeyUsage member has the bit flags you are interested in.
You can also look at the szOID_KEY_USAGE extension, which will use the same bit flags, but the msdn documentation states that those fields are
advisory field[s], only, and does not imply that usage of the key is restricted
to the purpose indicated
Depending on what you need the information for, you could use either extension.
With the help of Josh Poley, I found the answer. Thank you Josh
bool CertHasDigitalSignature(PCCERT_CONTEXT pCert)
{
bool retVal(false);
CERT_EXTENSION* keyUsage;
keyUsage = CertFindExtension(szOID_KEY_USAGE, pCert->pCertInfo->cExtension, pCert->pCertInfo->rgExtension);
if(NULL != keyUsage)
{
DWORD strSz(0);
if(CryptFormatObject(X509_ASN_ENCODING, 0, 0, NULL, szOID_KEY_USAGE, keyUsage->Value.pbData ,keyUsage->Value.cbData, NULL, &strSz))
{
std::wstring Buff;
Buff.resize((strSz / sizeof(wchar_t)) + 1);
if(CryptFormatObject(X509_ASN_ENCODING, 0, 0, NULL, szOID_KEY_USAGE, keyUsage->Value.pbData ,keyUsage->Value.cbData, (void*)Buff.data(), &strSz))
{
if (std::wstring::npos != Buff.find(L"Digital Signature"))
retVal = true;
}
}
}
return retVal;
}
Related
Windows 10 / C++ / Win32
I need to make a list of 'installed' Keyboards.
The components of my 'list' needs to contain:
The HKL for the keyboard.
The KLID for the keyboard.
The values of the registry values obtained from:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts
Example: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000409
Value: Layout File
Value: Layout Text
The only way I am aware of enumerating Keyboards is via GetKeyboardLayoutList(), which returns a list of HKL's.
Methodology which works for 'standard' keyboards (HKL's 04090409, 04070407,..).
TCHAR Buffer[50];
TCHAR Buffer2[50];
HKL LoadedKeyboardLayout;
// Hkl: 04090409
_stprintf_s(Buffer, (sizeof(Buffer)/sizeof(TCHAR)), _T("%08X"), (((UINT)Hkl >> 16) & 0xFFFF));
// Buffer: "0000409"
LoadedKeyboardLayout = LoadKeyboardLayout(Buffer, (UINT)0);
// It Loads
ActivateKeyboardLayout(LoadedKeyboardLayout, KLF_SETFORPROCESS);
// It Activates
GetKeyboardLayoutName(Buffer2);
// Buffer2: "00000409"
// I can now fish the registry HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000409
This DOES not work when the 'device identifier' is NOT 0000.
From LoadKeyboardLayout() documentation:
The name of the input locale identifier to load. This name is a string composed of the hexadecimal value of the Language Identifier (low word) and a device identifier (high word). For example, U.S. English has a language identifier of 0x0409, so the primary U.S. English layout is named "00000409". Variants of U.S. English layout (such as the Dvorak layout) are named "00010409", "00020409", and so on.
If one creates a 'Custom' keyboard, I find it is impossible to get the 'device identifier' from the custom keyboards HKL.
For instance:
Create a Custom Keyboard US with MKLCS.
Install it.
The 'KLID' will be 'A0000409'.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\a0000409
The HKL will be 0xF0C00409 (as returned within the HKL list generated by GetKeyboardLayoutList()).
In order to load the keyboard with LoadKeyboardLayout(), one needs 'A0000409'.
It does not seem possible to create A0000409 from F0C00409.
I also created my own keyboard layout without MKLCS.
I arbitrarily named it 00060409.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00060409
It's HKL is FFFE0409 (as returned within the HKL list generated by GetKeyboardLayoutList()).
It does not seem possible to create 00060409 from FFFE0409.
With all of that said, how does one obtain a KLID from an HKL?
Or, is there another way I can go about creating my list of installed keyboards?
//================================================================
11/25/2020 Addition.
Thank you Rita.
It seems as if GetKeyboardLayoutList() creates a list of 'Loaded' keyboard layouts.
System loaded keyboard layouts, 0x0409409,....
Keyboard Layouts installed via MKLCS installation.
It seems that any Keyboards defined within the following registry keys will get loaded at boot.
HKEY_CURRENT_USER\Keyboard Layout
Note the Preload and Substitute value's.
(Note there are many other 'Keyboard Layout' keys spread around within the registry, so I am
not sure if HKEY_CURRENT_USER\Keyboard Layout is the actual registry key that defines the
PreLoad - Substitutes.
HKEY_USERS\.DEFAULT\Keyboard Layout
HKEY_USERS\S-1-5-18 (My User Account)
HKEY_LOCAL_MACHINE\SYSTEM\Keyboard Layout
)
So there is no doubt in my mind that Rita's example works due to the fact that her HKEY_CURRENT_USER\Keyboard Layout contains the following entries:
Preload:
1 d0010804
Substitutes:
d0010804 a0000804
The entries may have been put there by the MKLCS installer?
Or perhaps the action of adding the keyboard via Settings->Time & Language->Click on Preferred Language->Options->Add a Keyboard
From ActivateKeyboardLayout() documentation:
The input locale identifier must have been loaded by a previous call to the LoadKeyboardLayout function.
Since a0000804 (HKL: F0C00804) is actually already loaded ActivateKeyboardLayout() works.
Since my KLID: 00060409 is not referenced within any of the X\Keyboard Layout Preload and Substitutes
I must physically call LoadKeyBoardLayout(L"00060409") in order for it to appear within the GetKeyboardList() HKL's.
Thanks again Rita.
how does one obtain a KLID from an HKL?
There seems no direct method out of box to achieve it.
A workaround is retrieving a list of HKLs, then active a layout via a HKL, then get its KLID. The following is an example:
int cnt = GetKeyboardLayoutList(sizeof(hklArr)/sizeof(hklArr[0]), hklArr);
if(cnt > 0)
{
printf("keyboard list: \n");
for (UINT i = 0; i < cnt; i++)
{
printf("%x\n", (LONG_PTR)hklArr[i]);
if (ActivateKeyboardLayout(hklArr[i], KLF_SETFORPROCESS))
{
WCHAR pName[KL_NAMELENGTH];
if (GetKeyboardLayoutName(pName))
{
wprintf(L"layout name (KLID): %s\n", pName);
}
}
}
}
Result like this:
Update:
Update 2: Share my creating and installing steps.
I use Keyboard Layout Creator 1.4.
Load an existing keyboard for testing purpose. (You can modify based on it or create your own completely.)
Valid and test keyboard to make sure it works as you expected. Then build DLL and setup package.
Run setup.exe generated by step 2. After installation complete you will see the related preload keyboard layout item in registry.
There is no safe way to do this since it is not documented and can be changed after Windows updates.
Proper algorithm is posted here.
Here is my code that is working on my Windows 10 Version 21H2 (November 2021 Update) so far:
// Returns KLID string of size KL_NAMELENGTH
// Same as GetKeyboardLayoutName but for any HKL
// https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-language-pack-default-values
BOOL GetKLIDFromHKL(HKL hkl, _Out_writes_(KL_NAMELENGTH) LPWSTR pwszKLID)
{
bool succeded = false;
if ((HIWORD(hkl) & 0xf000) == 0xf000) // deviceId contains layoutId
{
WORD layoutId = HIWORD(hkl) & 0x0fff;
HKEY key;
CHECK_EQ(::RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key), ERROR_SUCCESS);
DWORD index = 0;
while (::RegEnumKeyW(key, index, pwszKLID, KL_NAMELENGTH) == ERROR_SUCCESS)
{
WCHAR layoutIdBuffer[MAX_PATH] = {};
DWORD layoutIdBufferSize = sizeof(layoutIdBuffer);
if (::RegGetValueW(key, pwszKLID, L"Layout Id", RRF_RT_REG_SZ, nullptr, layoutIdBuffer, &layoutIdBufferSize) == ERROR_SUCCESS)
{
if (layoutId == std::stoul(layoutIdBuffer, nullptr, 16))
{
succeded = true;
DBGPRINT("Found KLID 0x%ls by layoutId=0x%04x", pwszKLID, layoutId);
break;
}
}
++index;
}
CHECK_EQ(::RegCloseKey(key), ERROR_SUCCESS);
}
else
{
WORD langId = LOWORD(hkl);
// deviceId overrides langId if set
if (HIWORD(hkl) != 0)
langId = HIWORD(hkl);
std::swprintf(pwszKLID, KL_NAMELENGTH, L"%08X", langId);
succeded = true;
DBGPRINT("Found KLID 0x%ls by langId=0x%04x", pwszKLID, langId);
}
return succeded;
}
Also you can use this code to enumerate layout profiles with KLID in LAYOUTORTIPPROFILE.szId:
typedef struct tagLAYOUTORTIPPROFILE {
DWORD dwProfileType;
LANGID langid;
CLSID clsid;
GUID guidProfile;
GUID catid;
DWORD dwSubstituteLayout;
DWORD dwFlags;
WCHAR szId[MAX_PATH];
} LAYOUTORTIPPROFILE;
// Flags used in LAYOUTORTIPPROFILE::dwProfileType
#define LOTP_INPUTPROCESSOR 1
#define LOTP_KEYBOARDLAYOUT 2
// Flags used in LAYOUTORTIPPROFILE::dwFlags.
#define LOT_DEFAULT 0x0001
#define LOT_DISABLED 0x0002
std::vector<LAYOUTORTIPPROFILE> EnumLayoutProfiles()
{
// http://archives.miloush.net/michkap/archive/2008/09/29/8968315.html
// https://learn.microsoft.com/en-us/windows/win32/tsf/enumenabledlayoutortip
typedef UINT(WINAPI* EnumEnabledLayoutOrTipFunc)(LPCWSTR pszUserReg, LPCWSTR pszSystemReg, LPCWSTR pszSoftwareReg, LAYOUTORTIPPROFILE* pLayoutOrTipProfile, UINT uBufLength);
static EnumEnabledLayoutOrTipFunc EnumEnabledLayoutOrTip = reinterpret_cast<EnumEnabledLayoutOrTipFunc>(::GetProcAddress(::LoadLibraryA("input.dll"), "EnumEnabledLayoutOrTip"));
if (!EnumEnabledLayoutOrTip)
return {};
const UINT count = EnumEnabledLayoutOrTip(nullptr, nullptr, nullptr, nullptr, 0);
std::vector<LAYOUTORTIPPROFILE> layouts;
layouts.resize(count);
const UINT written = EnumEnabledLayoutOrTip(nullptr, nullptr, nullptr, layouts.data(), count);
CHECK_EQ(count, written);
return layouts;
}
The LAYOUTORTIPPROFILE.szId string format of the layout is:
<LangID>:<KLID>
The string format of the text service profile (IME) is:
<LangID>:{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
More info here.
I have taken over maintenance of a legacy MFC OCX control in C++. The project is now in VS2013. I'm trying to understand the functioning of the DoPropExchange method. This method appears to be calling PX_?????(member) for nearly all the data members in the control where ???? is the type (Bool, Short, Long ...) My understanding is these are called for the purpose of providing persistent storage of properties. But from my understanding of the operation of the OCX control there are no persistent properties. Would there be any other reason to be calling PX_???? for all data members in DoPropExchange other than to support persistent properties? I'm also trying to understand where these persistent properties are loaded/stored. Where is the serialized file for loading/storing persistent property values specified?
Here is the source for DoPropExchange
// CSigPlusCtrl::DoPropExchange - Persistence support
void CSigPlusCtrl::DoPropExchange(CPropExchange* pPX)
{
DWORD Version;
long BaudRate;
short ComPort;
BOOL Rv;
LOG(("DoPropExchange Entry"));
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
Version = pPX->GetVersion();
if (pPX->IsLoading())
{
LoadDefaultProperties();
LoadIniParameters();
}
if ((Version & 0xFFFF0000) == (DWORD)MAKELONG(0, _wVerMajor))
{
Rv = PX_Short(pPX, _T("ImageFileFormat"), ImageFileFormat, 0);
Rv = PX_Short(pPX, _T("ImageXSize"), ImageXSize, 0);
Rv = PX_Short(pPX, _T("ImageYSize"), ImageYSize, 0);
Rv = PX_Short(pPX, _T("ImagePenWidth"), ImagePenWidth, 1);
. . .
Rv = PX_Short(pPX, _T("ZoomY"), ZoomY, 0);
Rv = PX_Short(pPX, _T("ZoomPower"), ZoomPower, 1);
if (pPX->IsLoading())
{
if (SigBlob != NULL)
{
GlobalFree(SigBlob);
SigBlob = NULL;
}
}
else
{
if (SigBlob == NULL)
{
SigBlobType* SigBlobPtr;
SigBlob = GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
SigBlobPtr = (SigBlobType*)GlobalLock(SigBlob);
SigBlobPtr->Size = 0;
GlobalUnlock(SigBlob);
}
}
if ((Version & 0xFFFF) == Version223)
{
Rv = PX_Blob(pPX, _T("SigBlob"), SigBlob, NULL);
}
if ((Version & 0xFFFF) >= Version224)
{
CString SigStr;
if (!pPX->IsLoading())
{
SigStr = BlobToString();
}
Rv = PX_String(pPX, _T("SigStringStored"), SigStr, _T(""));
if (pPX->IsLoading())
{
BlobFromString(SigStr);
}
}
}
else
{
SigMessageBox("Warning Incompatable Versions of SigPlus Control");
}
LoadTabletParameters();
LOG(("DoPropExchange Exit"));
}
EDIT Added 6-21-2018
Running in the debugger I observe that when DoPropExchange is called, VS2013 shows the stack with a message that stack frames below may be incorrect. And the one frame just above, that calls DoPropExchange, is from mfc120d.dll which does not have symbol file available mfc120d.i386.pdb.
This Microsoft Forum Post seems to indicate that the symbol file is not available for VS2015 and I'm wondering if that is also the case for VS2013. So far I have not been able to find place to download MFC120 symbols for debug.
Starting a bounty today to find someone who can explain how and where properties are normally serialized for OLE controls and what methods are used to specify the serialized data storage location/media. This is of concern because this control runs in a Citrix ZenDesk network environment in a Terminal Aware program and if properties are being stored somewhere then each client needs to specify a location unique to that client.
The DoPropExchange is used to implement control attribute persistence mainly between design and run-time. The actual destination sink is passed by the client of the OCX.
In VC the the settings are stored in RC file while in VB in frm and frx files. If you open FRM in notepad you would probably see the section with the properties of this control.
As a side note, there is a similar implementation in case the control is used in HTML with inline settings in he html itself.
Unless your VB clients saves the settings externally via direct calls to Property bag function it is not likely you have a issue here since the above properties are not stored at runtime.
I've seen lots of info on how to read game controller input using XInput but I really want to know the name of the controller that is connected.
How can I find out the name of connected controllers on a PC or more specifically the name of the controller I am reading XInput from?
You can do this by calling the joyGetDevCaps function which returns a JOYCAPS structure containing all information (including name) of the connected controller.
You can use DirectInput to get the name of the device. You need to do that using a callback:
pDirectInput->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoystickCallbackStatus, &joynum, DIEDFL_ATTACHEDONLY);
Then you have to be a bit creative: on Startup detect all devices using the callback and store their name/GUID... and then when a device is hot-plugged (which you detect with XInputGetState) look for the device which you don't know about yet, with a modified version of that earlier callback, something similar to this:
BOOL CALLBACK EnumJoystickCallbackStatus(LPCDIDEVICEINSTANCE pdevinst, LPVOID pref)
{
DWORD devtype = GET_DIDEVICE_TYPE(pdevinst->dwDevType);
DWORD subtype = GET_DIDEVICE_SUBTYPE(pdevinst->dwDevType);
if (devtype == DI8DEVTYPE_KEYBOARD || (devtype == DI8DEVTYPE_SUPPLEMENTAL && subtype == DI8DEVTYPESUPPLEMENTAL_UNKNOWN)) {
return DIENUM_CONTINUE;
}
ULONG* pjoynum = reinterpret_cast<ULONG*>(pref);
if (IsXInputDevice(&pdevinst->guidProduct)) {
// loop through your known devices and see if this GUI already exists
// we are looking for one which we don't know about yet.
if (!found) {
// store GUI / Name / ... in some global controllers-array
return DIENUM_STOP; // done
}
}
DEBUG_INFO(Debug::XDF_General, "continue");
return DIENUM_CONTINUE;
}
Note that if you have multiple xbox-controllers, you'll get a callback for each one separately.
Implementation of IsXInputDevice can be found in the MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx
Ho can i get user account name, that ran the process with specified id. Is there any api function for this?
I am using windows,c++.
There is not an API function that does this directly, however you can combine a few API calls to do this. Of course your program will need to satisfy any ACLs that are applied to the process that you are interested in examining.
First, given the process ID, you'll need to open a handle to the process. You can use OpenProcess for that, requesting the PROCESS_QUERY_INFORMATION access right.
Once you have that handle, you can call OpenProcessToken, requesting the TOKEN_QUERY access right.
Finally, you can then call GetTokenInformation, requesting the TokenUser information class, which will give you the user account of the token. This information is provided to you in the form of a SID. To convert the SID to the actual name of the account, you can call LookupAccountSid.
Don't forget to call CloseHandle on both the process handle and the token handle once you're finished with them.
And this is function using Aaron Klotz algorithm:
public static string GetProcessOwnerByID(int processId)
{
IntPtr processHandle = IntPtr.Zero;
IntPtr tokenHandle = IntPtr.Zero;
try
{
processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, false, processId);
if (processHandle == IntPtr.Zero)
return "NO ACCESS";
OpenProcessToken(processHandle, TOKEN_QUERY, out tokenHandle);
using (WindowsIdentity wi = new WindowsIdentity(tokenHandle))
{
string user = wi.Name;
return user.Contains(#"\") ? user.Substring(user.IndexOf(#"\") + 1) : user;
}
}
finally
{
if (tokenHandle != IntPtr.Zero) CloseHandle(tokenHandle);
if (processHandle != IntPtr.Zero) CloseHandle(processHandle);
}
}
Whole function (with constants) can be found on GitHub gist
I need to programmatically modify the Access Descriptors on a known Registry key during product installation. The way I want it to work is:
The installer is run in Administrative mode.
A Registry key is created.
A function (the one I need) queries the ACL from the key.
If this function finds that the group 'Users' already has write access, nothing should be done.
If not, it should add a new permission allowing write access to the 'Users' group.
The permissions are saved for the Registry key.
This question is similar to Setting Registry key write permissions using .NET, however, I need a C++/Win32 implementation.
Thanks in advance
For getting and setting the ACL of the key you need to use RegGetKeySecurity and RegSetKeySecurity. Then you need to iterate through the ACEs, examining any that apply to the "Users" group SID. Then you'll either modify/remove the existing one and/or add a new one. Be advised that working with ACLs in plain old Win32 C is a pain.
The smallest code to grant access consists of 3 API calls. It gives full access to the given hkey for all authenticated users and administrators.
This snippet does not contain proper error handling and reporting. Do not copy/paste it into the production code.
PSECURITY_DESCRIPTOR sd = nullptr;
ULONG sd_size = 0;
TCHAR* rights = TEXT( "D:" ) // Discretionary ACL
TEXT( "(A;OICI;GA;;;AU)" ) // Allow full control to all authenticated users
TEXT( "(A;OICI;GA;;;BA)" ); // Allow full control to administrators
ConvertStringSecurityDescriptorToSecurityDescriptor( rights, SDDL_REVISION_1, &sd, &sd_size );
RegSetKeySecurity( hkey, DACL_SECURITY_INFORMATION, sd );
LocalFree( sd );
Detecting if "Users" have write access to the key might be more difficult than expected. I ended up with writing a test value to the registry and checking the result of that write.
Just to expand on Mikhail Vorotilov's answer, and also drawing inspiration from the example code at
https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl
bool RegistryGrantAll(HKEY hKey)
{
bool bResult = false;
PSECURITY_DESCRIPTOR sd = nullptr;
const TCHAR* szSD =
TEXT("D:") // Discretionary ACL
TEXT("(D;OICI;KA;;;BG)") // Deny access to built-in guests
TEXT("(D;OICI;KA;;;AN)") // Deny access to anonymous logon
TEXT("(A;OICI;KRKW;;;AU)") // Allow KEY_READ and KEY_WRITE to authenticated users ("AU")
TEXT("(A;OICI;KA;;;BA)"); // Allow KEY_ALL_ACCESS to administrators ("BA" = Built-in Administrators)
if (ConvertStringSecurityDescriptorToSecurityDescriptor((LPCTSTR)szSD, SDDL_REVISION_1, &sd, 0))
{
auto result = RegSetKeySecurity(hKey, DACL_SECURITY_INFORMATION, sd);
if (ERROR_SUCCESS == result)
bResult = true;
else
SetLastError(result);
// Free the memory allocated for the SECURITY_DESCRIPTOR.
LocalFree(sd);
}
return bResult;
}
If the function returns false then call GetLastError() for more information on the failure cause.
Code compiles on VS2019 and appears to work.
I have not added code to check that hKey is a valid registry handle.
Edit: I've edited this a few times following testing. Sorry about all the edits. What I ended up with was far closer to Mikhail's answer than I started with.
Links to further info:
https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl
https://learn.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptorw
https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings
Sup, hope OP is still interested in the answer. Here is the working code adding ACEs to ACLs, it may be used to add ACEs to registry or filesystem DACLs. I haven't tried it with anything else yet. As you may notice, no nasty RegGetKeySecurity or manual ACL composing needed. There's even no need to RegOpenKeyEx. For more info, check this MS doc.
UPD Of course it will need admin rights for execution.
// sk - alloced string / path to needed key
// common look: MACHINE\\Software\\... where MACHINE == HKEY_LOCAL_MACHINE
// google for more address abbrevations
PSECURITY_DESCRIPTOR pSD = 0;
EXPLICIT_ACCESS ea;
PACL pOldDACL = 0, pNewDACL = 0;
if (ERROR_SUCCESS == GetNamedSecurityInfo(sk, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, 0, 0, &pOldDACL, 0, &pSD)) {
memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = KEY_ALL_ACCESS;
ea.grfAccessMode = GRANT_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.ptstrName = <USERNAME HERE>; //DOMAIN\\USERNAME
if (ERROR_SUCCESS == SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL)) {
if (ERROR_SUCCESS == SetNamedSecurityInfo(sk, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, 0, 0, pNewDACL, 0)) {
if (pSD != 0) LocalFree((HLOCAL)pSD);
if (pNewDACL != 0) LocalFree((HLOCAL)pNewDACL);
SAFE_FREE(sk);
// WE'RE GOOD!
return ... ;
} else {
if (pSD) LocalFree((HLOCAL)pSD);
if (pNewDACL) LocalFree((HLOCAL)pNewDACL);
SAFE_FREE(sk);
// SetNamedSecurityInfo failed
return ... ;
}
} else {
if (pSD) LocalFree((HLOCAL)pSD);
if (pNewDACL) LocalFree((HLOCAL)pNewDACL);
SAFE_FREE(sk);
// SetEntriesInAcl failed
return ... ;
}
} else {
if (pSD) LocalFree((HLOCAL)pSD);
SAFE_FREE(sk);
// GetNamedSecurityInfo failed
return ... ;
}