I would like to retrieve the effective rights of a given registry key as a parameter. I test with an existing registry key under Windows. To do that I use the methods CreateFile, GetSecurityInfo and GetEffectiveRightsFromAclA.
I wanted to know if this is the correct method, because I have an error for the CreateFile method that returns INVALID_HANDLE_VALUE. Moreover for the method GetEffectiveRightsFromAclA, I do not understand which parameter I must put in TRUSTEE_A?
LPCWSTR lpwRegistryKey = L"HKEY_CLASSES_ROOT\\.acc\\OpenWithProgids\\WMP11.AssocFile.ADTS";
HANDLE handleKey;
handleKey = CreateFile(lpwRegistryKey, GENERIC_READ, FILE_SHARED_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if(handleKey == INVALID_HANDLE_VALUE)
{
//qDebug()<<"error";
}
//Here is an user SID
PSID pUserSid;
QString sSid("S-4-5-12");
BOOL resultSidConvert = ConvertStringSidToSidA(sSid.toStdString().c_str(), &pUserSid);
//Here success
if(resultSidConvert != 0)
{
PACL pDacl;
PSECURITY_DESCRIPTOR pSecurityDescriptor;
DWORD dwResultSecurity = GetSecurityInfo(handleKey, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, nullptr, &pUserSid, &pDacl, nullptr, &pSecurityDescritptor);
if(dwResultSecurity == ERROR_SUCCESS)
{
ACCESS_MASK pAccessMask;
TRUSTEE_A pTrustee; //How should I initialize pTrustee?
DWORD dwEffectiveRights = (GetEffectiveRightsFromAclA(pDacl, &pTrustee, &pAccessMask);
if(dwEffectiveRights == ERROR_SUCCESS)
{
if((pAccessMask & DELETE) == DELETE)
{
qDebug()<<"Delete";
}
if((pAccessMask & GENERIC_READ) == GENERIC_READ)
{
qDebug()<<"READ";
}
//etc ..........
}
}
}
A registry key is not a file!
Don't use CreateFile. Use RegOpenKeyEx instead.
First, you must run the application as admin.
Then, you can open the key successfully. After that, take a look at RegGetKeySecurity function.
Related
I want to recover the effective rights on a shared folder. To do that I used the methods CreateFile, GetSecurityInfo and GetEffectiveRightsFromAclA. The problem is that when I test the rights it does not work while I set the SID of the administrator. Does this come from a bad implementation of my code? I specify that I have the rights to the file.
//stringName is the file path
HANDLE file = CreateFile(stringName.c_str(), GENERIC_READ,
FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
PSID pSid;
//The SID is that of the administrator
ConvertStringSidToSidA(stringSid.c_str(), &pSid);
PACL ppDacl;
PSECURITY_DESCRIPTOR ppSecurityDescriptor;
GetSecurityInfo(file, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr,
&pSid, &ppDacl, nullptr, &ppSecurityDescriptor);
ACCESS_MASK pAccesMask;
TRUSTEE_A pTrustee;
GetEffectiveRightsFromAclA(ppDacl, &pTrustee, &pAccesMask);
//here conditions are not respected
if((pAccesMask & GENERIC_READ) == GENERIC_READ)
{
std::cout<<"ok"<<std::endl;
}
if((pAccesMask & GENERIC_WRITE) == GENERIC_WRITE)
{
std::cout<<"ok"<<std::endl;
}
if((pAccesMask & GENERIC_EXECUTE) == GENERIC_EXECUTE)
{
std::cout<<"ok"<<std::endl;
}
if((pAccesMask & GENERIC_ALL) == GENERIC_ALL)
{
std::cout<<"ok"<<std::endl;
}
Before use the pAccesMask value make sure the GetEffectiveRightsFromAclA function return ERROR_SUCCESS.
And because a Windows file object maps the GENERIC_READ bit to the READ_CONTROL and SYNCHRONIZE standard access rights. So change the condition check logic for GENERIC_READ like this:
if ((pAccesMask & READ_CONTROL) == READ_CONTROL && (pAccesMask & SYNCHRONIZE) == SYNCHRONIZE) //GENERIC_READ
{
std::cout << "GENERIC_READ" << std::endl;
}
I am writing a program that needs to create/delete a registry key. I am having a problem that the code to create the key returns ERROR_INVALID_FUNCTION.
If you look in the registry the key has been created so I'm not sure what the impact of this error is.
The value of key is "SOFTWARE\\Wow6432Node\\COMPANY\\APPLICATION"
The code is:
int RegistryViewer::CreateRegistryLocation(const char* key)
{
HKEY hkey = 0;
int retVal = RegistryViewer::OpenRegistryLocation(key);
if(retVal != ERROR_SUCCESS)
{
retVal = RegCreateKeyEx(HKEY_LOCAL_MACHINE, CString(key), 0, NULL,REG_OPTION_NON_VOLATILE, KEY_WOW64_32KEY | KEY_WRITE, NULL, &hkey, NULL);
RegCloseKey(hkey);
}
return retVal;
}
Is the problem that although the key is created, it's unable to set the permissions correctly?
Thanks.
Going to answer my own question in case it's helpful to others in future. I think I had two problems that were limiting access to the registry key, firstly I was opening it twice (once to see it existed and then when it was created), secondly I was assigning it KEY_WRITE permissions. Since RegCreateKeyEx opens a key if it already exists the first check was pointless and may have been holding the resource. I'm not sure the second thing was a problem but since it's changed in the code I thought I'd mention it.
Working code looks like this:
//Create a registry location
int RegistryViewer::CreateRegistryLocation(const char* key)
{
HKEY hkey = 0;
int retVal = RegCreateKeyEx(HKEY_LOCAL_MACHINE, CString(key), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WOW64_32KEY | KEY_ALL_ACCESS, NULL, &hkey, NULL);
if(retVal == ERROR_SUCCESS)
RegCloseKey(hkey);
return retVal;
}
The comment by eryksun was useful for another issue though as I was manually checking which part of the registry to use and he enabled me to remove that check.
I'm a Domain Admin and I want to take ownership of some shared folders on some server of my domain programmatically in API(for example C++). I did some reading work and found that a Domain Admin is in the member machine's Local Admins group by default, and the Local Admins users can take ownership anyway. I just wrtie some code in this way but still encountered ERROR_ACCESS_DENIED when getting the owner sid using GetNamedSecurityInfo? Where's the problem?
Something interesting is: When I changed the GetNamedSecurityInfo's secound argument from SE_FILE_OBJECT to SE_LMSHARE, it would succeed(also set one). But I didn't see the owner changed in the "Security" tab of folder's properties. I know a "share" permission is different with "security" one. a "share" permission even don't have a owner. So what owner did I get when calling GetNamedSecurityInfo by the SE_LMSHARE argument?
Here's the function i use for Taking ownership for the folder "strFileName", on server "strServerName", the owner changed to is just the Domain Admin account known as "strDomainName" "strUserName" "strPassword", orginal owner is reserved in "pOriginSID".
I got error code 5 in the GetNamedSecurityInfo call (also the Set one). I also write a impersonation method "logOnByUserPassword" which seems not to work, i paste it below.
HANDLE ADPermissionSearch::getAccessTokenByCredential(CString strDomainName, CString strUserName, CString strPassword)
{
CString strUPNUserName = strUserName + _T("#") + strDomainName;
HANDLE hToken;
BOOL bResult;
//bResult = LogonUser(strUserName, strDomainName, strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
// &hToken);
if (strDomainName != _T(""))
{
bResult = LogonUser(strUPNUserName, _T(""), strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
&hToken);
}
else
{
bResult = LogonUser(strUserName, _T("."), strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
&hToken);
}
if (bResult == FALSE)
{
MyMessageBox_Error(_T("getAccessTokenByCredential Error."), _T("Error"));
return FALSE;
}
else
{
return hToken;
}
}
int ADPermissionSearch::takeOwnership(CString strServerName, CString strFileName, CString strDomainName, CString strUserName, CString strPassword, __out PSID &pOriginSID)
{
CString strUNCFileName = _T("\\\\") + strServerName + _T("\\") + strFileName;
_bstr_t bstrUNCFileName = _bstr_t(strUNCFileName);
PSID pSIDAdmin = NULL;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
HANDLE hToken = NULL;
DWORD dwRes;
// Create a SID for the BUILTIN\Administrators group.
if (!AllocateAndInitializeSid(&SIDAuthNT, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pSIDAdmin))
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
// If the preceding call failed because access was denied,
// enable the SE_TAKE_OWNERSHIP_NAME privilege, create a SID for
// the Administrators group, take ownership of the object, and
// disable the privilege. Then try again to set the object's DACL.
// Open a handle to the access token for the calling process.
/*
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
&hToken))
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
*/
if ((hToken = getAccessTokenByCredential(strDomainName, strUserName, strPassword)) == NULL)
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
// Enable the SE_TAKE_OWNERSHIP_NAME privilege.
if (!setPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, TRUE))
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
// Get the original owner in the object's security descriptor.
dwRes = GetNamedSecurityInfo(
bstrUNCFileName, // name of the object
SE_FILE_OBJECT, // type of object
OWNER_SECURITY_INFORMATION, // change only the object's owner
&pOriginSID, // SID of Administrator group
NULL,
NULL,
NULL,
NULL);
if (dwRes != ERROR_SUCCESS)
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
// Set the owner in the object's security descriptor.
dwRes = SetNamedSecurityInfo(
bstrUNCFileName, // name of the object
SE_FILE_OBJECT, // type of object
OWNER_SECURITY_INFORMATION, // change only the object's owner
pSIDAdmin, // SID of Administrator group
NULL,
NULL,
NULL);
if (dwRes != ERROR_SUCCESS)
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
// Disable the SE_TAKE_OWNERSHIP_NAME privilege.
if (!setPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, FALSE))
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
return 1;
}
BOOL ADDirectorySearch::logOnByUserPassword(CString strDomainName, CString strUserName, CString strPassword)
{
CString strUPNUserName = strUserName + _T("#") + strDomainName;
HANDLE hToken;
BOOL bResult;
//bResult = LogonUser(strUserName, strDomainName, strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
// &hToken);
if (strDomainName != _T(""))
{
bResult = LogonUser(strUPNUserName, _T(""), strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
&hToken);
}
else
{
bResult = LogonUser(strUserName, _T("."), strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
&hToken);
}
if (bResult == FALSE)
{
MyMessageBox_Error(_T("logOnByUserPassword Error."), _T("Error"));
return FALSE;
}
else
{
bResult = ImpersonateLoggedOnUser(hToken);
if (bResult == FALSE)
{
MyMessageBox_Error(_T("logOnByUserPassword Error."), _T("Error"));
return FALSE;
}
else
{
return TRUE;
}
}
}
Local admins are subject to the usual Windows security checks with one exception: they can always take ownership of a secured object regardless of the permissions. This ensures that admins are always able to regain control.
However, you are not trying to take ownership, you are trying to read the current owner and you don't necessarily have permission to do that.
It's not clear from your code why you are trying to read the owner. You don't seem to do anything with it. Maybe remove the call to GetNamedSecurityInfo altogether.
Update
The intention is to write a program that checks the DACLs on every share. So it needs to save the current owner, take ownership, read the DACLs and restore the owner. But the current owner cannot be read until ownership has been taken.
I think this behaviour is by design. The original intention was that admins were able to take ownership, but not hide the fact that they had from the owner of an object, though there are ways around this. For example, for files you can read the complete security descriptor (including the owner) by enabling the backup privilege, calling BackupRead and parsing the output (a sequence of WIN32_STREAM_ID structures each followed by data). I don't know if there's a simpler way.
Information about shares is stored in the registry under:
SYSTEM\CurrentControlSet\Services\LanmanServer\Shares
The security info seems to be stored in the Security subkey, in a value named after the share. This binary value seems to be a security descriptor so you can read the owner with GetSecurityDescriptorOwner. You can also read all the other security info from this security descriptor, so you shouldn't need to change the owner at all.
I just create new registry key called MyTestApp, and want to add new data in it, but don't know how. So I have following code.
bool CreateSectionDataRegistry(CString sectionData, CString sectionValue)
{
HKEY hKey; //registry key handle
LONG lResult; //result of registry operations
DWORD dwType, dwSize=0;
//try to open the key that we are currently pointing at with rootPath
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, rootPath + "\\" + "MyTestApp", NULL, KEY_ALL_ACCESS, &hKey);
if (lResult == ERROR_SUCCESS || lResult == ERROR_MORE_DATA)
{
//we have successfully opened the registry key. Now try to access the data
lResult = RegQueryValueEx(HKEY_LOCAL_MACHINE, sectionData, 0, &dwType, NULL, &dwSize);
if(lResult == ERROR_SUCCESS || lResult == ERROR_MORE_DATA)
{
//data already exists, so just return
RegCloseKey(hKey);
return true;
}
else//section data does not exist, so create new data
{
DWORD dwDisposition;
lResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE, sectionData, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwDisposition);
RegCloseKey(hKey);
return true;
}
}
return false;
}
But when I trace down to the RegCreateKeyEx() call, it returns success but data is not created under the MyTestApp registry key or anywhere in the registry. Am I using the correct function to create new data under MyTestApp. Also from the argument above, I have CString sectionValue, so how do I check if I need to create data for REG_SZ or REG_DWORD?
Thanks!
It seems I just need to call RegSetValueEx() to create registry value under MyTestApp. Probably just me but when I look at the RegSetValueEx() definition in Microsoft website, it says set the value, but it does not say if value does not exist, it will create the value.
LPTSTR lpszData = new TCHAR[sectionValue.GetLength() + 1];
_tcscpy(lpszData, sectionValue);
lResult = RegSetValueEx(hKey, sectionData, 0, REG_SZ, (LPBYTE)lpszData, sectionValue.GetLength());
So now it creates the registry value, however I cannot still tell if sectionValue is REG_SZ or REG_DWORD.
The MSDN says that using ReadDirectoryChangesW implies the calling process having the Backup and Restore privileges.
Does this mean that only process launched under administrator account will work correctly?
I've tried the following code, it fails to enable the required privileges when running as a restricted user.
void enablePrivileges()
{
enablePrivilege(SE_BACKUP_NAME);
enablePrivilege(SE_RESTORE_NAME);
}
void enablePrivilege(LPCTSTR name)
{
HANDLE hToken;
DWORD status;
if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp = { 1 };
if( ::LookupPrivilegeValue(NULL, name, &tp.Privileges[0].Luid) )
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
BOOL result = ::AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
verify (result != FALSE);
status = ::GetLastError();
}
::CloseHandle(hToken);
}
}
Am I doing something wrong? Is there any workaround for using ReadDirectoryChangesW from a non-administrator user account? It seems that the .NET's FileSystemWatcher can do this. Thanks!
Update: Here is the full code of the class:
class DirectoryChangesWatcher
{
public:
DirectoryChangesWatcher(wstring directory)
{
enablePrivileges();
hDir = ::CreateFile(directory.c_str(),
FILE_LIST_DIRECTORY | FILE_FLAG_OVERLAPPED,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
ensure (hDir != INVALID_HANDLE_VALUE, err::SystemException);
::ZeroMemory(&overlapped, sizeof(OVERLAPPED));
overlapped.hEvent = dirChangedEvent.getHandle();
}
~DirectoryChangesWatcher() { ::CloseHandle(hDir); }
public:
Event& getEvent() { return dirChangedEvent; }
FILE_NOTIFY_INFORMATION* getBuffer() { return buffer; }
public:
void startAsyncWatch()
{
DWORD bytesReturned;
const BOOL res = ::ReadDirectoryChangesW(
hDir,
&buffer,
sizeof(buffer),
TRUE,
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE,
&bytesReturned,
&overlapped,
NULL);
ensure(res != FALSE, err::SystemException);
}
private:
void enablePrivileges()
{
enablePrivilege(SE_BACKUP_NAME);
enablePrivilege(SE_RESTORE_NAME);
}
void enablePrivilege(LPCTSTR name)
{
HANDLE hToken;
DWORD status;
if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp = { 1 };
if( ::LookupPrivilegeValue(NULL, name, &tp.Privileges[0].Luid) )
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
BOOL result = ::AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
verify (result != FALSE);
status = ::GetLastError();
}
::CloseHandle(hToken);
}
}
private:
HANDLE hDir;
OVERLAPPED overlapped;
Event dirChangedEvent;
FILE_NOTIFY_INFORMATION buffer[1024];
};
}
Update: Good news! It turned out the problem really was in the FILE_SHARE_WRITE flag in the call to CreateFile. The notifications did not come unless I was an admin. When I removed this flag, everything is now working ona non-admin account too.
I have used ReadDirectoryChangesW without requiring administrator rights, at least on Vista. I don't think you need to manually elevate the process in order to use it on a folder the user already has permissions to see.
It would be more helpful to see the actual code you are using to call ReadDirectoryChangesW, including how you create the handle you pass in.
I don't see where MSDN says you need either backup or restore privileges. It instructs you to call CreateFile with the File_Flag_Backup_Semantics flag set, and in that flag's description, MSDN says this:
The system ensures that the calling process overrides file security checks when the process has SE_BACKUP_NAME and SE_RESTORE_NAME privileges.
The way I read it, if you have those privileges, then the system will override the file security checks for you. So if you don't have those privileges, then the program will simply continue to be bound by whatever file security checks would ordinarily be in effect.
Alex, in your CreateFile() call you put FILE_FLAG_OVERLAPPED into wrong position. It should be moved from 2nd to 6th parameter.