Set file owner to non-existing user/SID in windows - c++

I'm trying to write a backup and recovery tool. I'm running my code on a WinPE CD (http://en.wikipedia.org/wiki/Windows_Preinstallation_Environment). I'm trying to read the entire C: partition and write it to the network. Just like the tar command, but windows specific. I have everything working except for setting the file owner. Windows seems to be really intolerant to files being owned by unknown SIDs. Since I'm running in WinPE, most of the users defined on C: aren't in the local user database.
Here are some of the functions I've tried:
SetFileSecurity (returns 1307)
SetSecurityInfo (returns 1307)
SetNamedSecurityInfo (returns 1307)
BackupWrite (returns 1307)
NtSetSecurityObject (returns 0xC000005A)
I know this can be done. SetACL (http://helgeklein.com/setacl/) is able to do it.
So, the question. How do I set the owner of a file to a non-existing user/SID? Any help is greatly appreciated!
Here's a sample of the code I've tried:
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <sddl.h>
#include <aclapi.h>
#include <tchar.h>
INT _tmain(){
PSECURITY_DESCRIPTOR psdOwner = LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH);
if (InitializeSecurityDescriptor(psdOwner,SECURITY_DESCRIPTOR_REVISION)){
PSID psOwner = (PSID)0;
if (ConvertStringSidToSid(TEXT("S-1-5-21-3626571138-2175758104-1447827851-1013"),&psOwner)){
if (SetSecurityDescriptorOwner(psdOwner,psOwner,FALSE)){
DWORD dwError = SetNamedSecurityInfo(TEXT("test.txt"),SE_FILE_OBJECT,OWNER_SECURITY_INFORMATION,psdOwner,NULL,NULL,NULL);
if (dwError == ERROR_SUCCESS){
_tprintf(TEXT("Success!\n"));
}else{
_tprintf(TEXT("Failed to set owner: %u\n"),dwError);
}
}else{
_tprintf(TEXT("Failed to set owner into SD: %u\n"),GetLastError());
}
}else{
_tprintf(TEXT("Failed to covnert Sid string to Sid: %u\n"),GetLastError());
}
if (psOwner) LocalFree(psOwner);
}else{
_tprintf(TEXT("Failed to initialize SD: %u\n"),GetLastError());
}
if (psdOwner) LocalFree(psdOwner);
return 0;
}

Turns out you need SE_RESTORE_NAME token privilege. You can adjust your process token with the following:
BOOL TakeSecurityPriv(LPCTSTR szPriv){
BOOL bReturn = FALSE;
HANDLE hProcToken = (HANDLE)0;
if (OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hProcToken)){
TOKEN_PRIVILEGES tpTokPriv;
if (LookupPrivilegeValue(NULL,szPriv,&tpTokPriv.Privileges[0].Luid)){
tpTokPriv.PrivilegeCount = 1;
tpTokPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (AdjustTokenPrivileges(hProcToken,FALSE,&tpTokPriv,0,NULL,0)){
bReturn = TRUE;
}
}
}
return bReturn;
}
Then you can add the following to the beginning of main in my example:
if (TakeSecurityPriv(SE_RESTORE_NAME)){
That gets rid of the 1307 error. Unfortunately there is another bug in my example because the owner isn't set to the correct SID. However, when I switch back to BackupRead/BackupWrite I won't have to create a SID or SECURITY_DESCRIPTOR. I've pondered over what could be the issue for a while now with no luck. Perhaps someone else could answer that part.

Related

How do you identify "File Explorer" instances in an `IShellWindows` collection?

I'm trying to get a list of all "File Explorer" instances currently running. It's fairly straight forward getting a list that includes all instances, but I find myself running into a brick wall filtering that list to only "File Explorer" instances.
The following piece of code retrieves all Explorer instances, meaning both "File Explorer" and "Internet Explorer":
#include <comdef.h>
#include <ExDisp.h>
#include <ShlGuid.h>
#include <Windows.h>
#include <cstdio>
using _com_util::CheckError;
using std::puts;
_COM_SMARTPTR_TYPEDEF(IShellWindows, __uuidof(IShellWindows));
int main()
{
CheckError(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
// Acquire IShellWindows interface
IShellWindowsPtr spShellWindows{};
CheckError(spShellWindows.CreateInstance(CLSID_ShellWindows, nullptr, CLSCTX_LOCAL_SERVER));
// Request iterator
IUnknownPtr spEnum{};
CheckError(spShellWindows->_NewEnum(&spEnum));
IEnumVARIANTPtr spEnumVariant{};
CheckError(spEnum.QueryInterface(__uuidof(spEnumVariant), &spEnumVariant));
// Iterate over shell windows ...
while (true) {
variant_t var{};
// ... one element at a time
HRESULT hr = spEnumVariant->Next(1, &var, nullptr);
CheckError(hr);
// Iterator depleted?
if (hr == S_FALSE) break;
// Did we get the expected `IDispatch` interface?
if (var.vt != VT_DISPATCH) continue;
IDispatchPtr spDisp{};
spDisp.Attach(var.pdispVal, true);
puts("Got suspect; need ID");
// That was easy; now on to some real challenges
}
}
The obvious attempt
My first take at the problem was to just get rid of everything that isn't "File Explorer". Asking for the IWebBrowser2 interface would certainly only get an affirmative response from objects that actually are web browsers. Adding the following to the code above:
_COM_SMARTPTR_TYPEDEF(IWebBrowser2, __uuidof(IWebBrowser2));
// ...
int main()
{
// ...
IWebBrowser2Ptr spWebBrowser{};
hr = spDisp.QueryInterface(__uuidof(spWebBrowser), &spWebBrowser);
if (SUCCEEDED(hr)) puts("Implements IWebBrowser2");
// ...
After making the changes and running the code while an "Internet Explorer" instance is running produces the desired output. However, running the code while a "File Explorer" instance is running produces the same output! That's a surprise and a disappointment, all at the same time.
More robust, less useful
Exluding objects that can be identified as "not File Explorer" didn't work out. Let's try to only include objects that can be identified as "File Explorer" instead. That sounds even more obvious, but as we've learned, "obvious" and "not" go hand in hand when it comes to the Windows Shell.
I haven't actually implemented this, but the IShellWindows interface provides an Item method that can return only objects that match a particular ShellWindowTypeConstants (e.g. SWC_EXPLORER or SWC_BROWSER). Or return an object at a particular index in the window collection. But not BOTH!
So, yes, (potentially) more robust, but also less useful as it doesn't meet my requirements as soon as more than one instances of "File Explorer" are running. Bummer.
Circumstantial evidence
While neither of the above led anywhere, I started over and went on a full-blown investigation looking for hints. Since "File Explorer" browses the Shell namespace, there may be something to that account. The following outlines the approach, based on an article by Raymond Chen titled A big little program: Monitoring Internet Explorer and Explorer windows, part 1: Enumeration:
Starting from the IDispatch interface above, ask for a service with ID SID_STopLevelBrowser to get an IShellBrowser interface.
Call IShellBrowser::QueryActiveShellView to get an IShellView interface.
Ask the IShellView whether it implements something Shell-namespace-y, e.g. IPersistIDList.
If it does, conclude that we're holding a reference to a "File Explorer" instance.
This appears to produce the desired result, though it's not clear to me future-proof this is or when it stops working. Leaving aside how overly convoluted this appears, I'm concerned about its reliability.
Question
What's the recommended/robust/reliable way to identify all "File Explorer" instances in an IShellWindows collection? I will favor solutions based on official documentation, though I understand that this is the Windows Shell and there's next to no documentation at all.
Here's a possibility ...
#include <comdef.h>
#include <ExDisp.h>
#include <ShlGuid.h>
#include <Windows.h>
#include <cstdio>
#include <atlbase.h>
#include <string>
using _com_util::CheckError;
using std::puts;
using std::string;
_COM_SMARTPTR_TYPEDEF(IShellWindows, __uuidof(IShellWindows));
//nicked from StackOverflow
std::string ProcessIdToName(DWORD_PTR processId)
{
std::string ret;
HANDLE handle = OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION,
FALSE,
processId /* This is the PID, you can find one from windows task manager */
);
if (handle)
{
DWORD buffSize = 1024;
CHAR buffer[1024];
if (QueryFullProcessImageNameA(handle, 0, buffer, &buffSize))
{
ret = buffer;
}
else
{
printf("Error GetModuleBaseNameA : %lu", GetLastError());
}
CloseHandle(handle);
}
else
{
printf("Error OpenProcess : %lu", GetLastError());
}
return ret;
}
int main()
{
CheckError(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
// Acquire IShellWindows interface
IShellWindowsPtr spShellWindows{};
CheckError(spShellWindows.CreateInstance(CLSID_ShellWindows, nullptr, CLSCTX_LOCAL_SERVER));
// Request iterator
IUnknownPtr spEnum{};
CheckError(spShellWindows->_NewEnum(&spEnum));
IEnumVARIANTPtr spEnumVariant{};
CheckError(spEnum.QueryInterface(__uuidof(spEnumVariant), &spEnumVariant));
// Iterate over shell windows ...
while (true) {
variant_t var{};
// ... one element at a time
HRESULT hr = spEnumVariant->Next(1, &var, nullptr);
CheckError(hr);
// Iterator depleted?
if (hr == S_FALSE) break;
// Did we get the expected `IDispatch` interface?
if (var.vt != VT_DISPATCH) continue;
IDispatchPtr spDisp{};
spDisp.Attach(var.pdispVal, true);
puts("Got suspect; need ID");
// That was easy; now on to some real challenges
CComPtr<IWebBrowser2> lpWB;
spDisp->QueryInterface(&lpWB);
SHANDLE_PTR hWnd{ 0 };
lpWB->get_HWND(&hWnd);
if (hWnd)
{
DWORD pid = 0;
GetWindowThreadProcessId((HWND)hWnd, &pid);
if (pid != 0)
{
puts("pid");
auto s = ProcessIdToName((DWORD_PTR)pid);
puts(s.c_str());
}
}
}
}

How to get attribute of a directory like last access and last modify

I wanted to get timestamps of a directory and then show it to the user. I have written the following function, but it doesn't work when I give a full path of a directory to present its timestamp like access time. What should I do to fix this issue? I didn't know how should I open a directory and get information about its timestamps correctly for regular files my code works fine but when I wanted to extract information about directory it doesn't work.
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <strsafe.h>
BOOL GetLastWriteTimeDirectory(HANDLE arg_h_file, LPSTR arg_lpsz_string, DWORD arg_dw_size)
{
FILETIME ft_CreateTime, ft_AccessTime, ft_WriteTime;
SYSTEMTIME st_UTC, st_Local;
DWORD dw_Return;
// Retrieve the file times for the file.
if (!GetFileTime(arg_h_file, &ft_CreateTime, &ft_AccessTime, &ft_WriteTime))
{
return FALSE;
}
// Convert the last-write time to local time.
FileTimeToSystemTime(&ft_WriteTime, &st_UTC);
SystemTimeToTzSpecificLocalTime(NULL, &st_UTC, &st_Local);
// Build a string showing the date and time.
dw_Return = StringCchPrintfA(arg_lpsz_string, arg_dw_size, "%02d/%02d/%d %02d:%02d", st_Local.wMonth, st_Local.wDay, st_Local.wYear, st_Local.wHour, st_Local.wMinute);
if (S_OK == dw_Return)
{
return TRUE;
}
else
{
return FALSE;
}
}
bool AttributeLastAccessDirectory(const char* arg_path)
{
HANDLE handleFile;
char bufferLastAccessTime[MAX_PATH];
char pathDirectory[MAX_PATH];
strcpy(pathDirectory, arg_path);
handleFile = CreateFileA(pathDirectory, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (handleFile == INVALID_HANDLE_VALUE)
{
return false;
}
if (GetLastWriteTimeDirectory(handleFile, bufferLastAccessTime, MAX_PATH))
{
printf("\n\t\t");
printf("%s", "Last Accessed: \t");
printf("%s\n", bufferLastAccessTime);
CloseHandle(handleFile);
return true;
}
CloseHandle(handleFile);
return false;
}
int main(int argc, char* argv[])
{
AttributeLastAccessDirectory("C:\\Users\\mkahs\\Desktop\\Sample\\");
return 0;
}
According to the documentation for CreateFileA:
To open a directory using CreateFile, specify the FILE_FLAG_BACKUP_SEMANTICS flag as part of dwFlagsAndAttributes. Appropriate security checks still apply when this flag is used without SE_BACKUP_NAME and SE_RESTORE_NAME privileges.
Your CreateFileA function call currently sets the dwFlagsAndAttributes parameter (the sixth parameter) to 0. Setting it to FILE_FLAG_BACKUP_SEMANTICS should fix the problem.
Also, there is no need for the pathDirectory array and the call to strcpy that sets it. The arg_path parameter can be passed to CreateFileA directly.

Can not format data in PWSTR variable to use in CopyFileW command

I am very new to this, sorry about the horrible code below. I am trying to get the default path for FOLDERID_Profile and then add "\test.exe" to the end of it. Then i need to use this as the path to copy a file to. I was able to use the pSHGetKnownFolderPath method to store the profiles directory in PWSTR user_dir . Problem is, this is not an acceptable string format for the copy function.
So i used the following code to very crudely attempt to convert it to something the copy function could use.
strcat((char *)user_dir,"\\test.exe");
test7 = (LPCWSTR)user_dir;
MessageBox(NULL,test7,L"WR test file",MB_OK);
i'm using a message box to check the path before using CopyFile(currentpath,test7,false); But this is giving me 㩃瑜獥⹴硥 . I am currently using
CopyFileW(currentpath,L"C:\\Users\\Jenia\\test.exe",false);
as a workaround, but I really need this to work on other computers too...
I know I am messing up my ANSI vs Unicode formatting again, please tell me how to best achieve this goal. Let me know if you would like me to post the entire code block, but until i run that strcat method user_dir has the correct path just no file name for copy method.
more complete code below:
#include <windows.h>
#include <shlwapi.h>
#include <stdio.h>
#include <Shlobj.h>
LPCWSTR test7 = 0;
PWSTR user_dir = 0;
HMODULE hndl_shell32;
lpSHGetKnownFolderPath pSHGetKnownFolderPath;
hndl_shell32 = LoadLibrary(L"shell32");
if (NULL != hndl_shell32)
{
pSHGetKnownFolderPath = (lpSHGetKnownFolderPath)
GetProcAddress(hndl_shell32, "SHGetKnownFolderPath");
if(pSHGetKnownFolderPath != NULL)
{
if (SUCCEEDED(pSHGetKnownFolderPath(
FOLDERID_Profile,
0,
NULL,
&user_dir)))
{
//I think this is the problem here
strcat((char *)user_dir,"\\test.exe");
test7 = (LPCWSTR)user_dir;
MessageBox(NULL,test7,L"WR test file",MB_OK);
}
}
else
{
fprintf(stderr, "Failed to locate function: %d\n",
GetLastError());
}
}
else
{
fprintf(stderr, "Failed to load shell32.dll: %d\n", GetLastError());
}
Too many errors here. You cannot strcat on pointer filled by SHGetKnownFolderPath. Assuming that all variables are Unicode, this should work with project with any character set:
LPWSTR test7 = 0;
WCHAR user_dir[MAX_PATH];
...
SHGetKnownFolderPath(... &test7);
...
wcscpy(user_dir, test7);
wcscat(user_dir, L"\\test.exe");
MessageBoxW(NULL,test7,L"WR test file",MB_OK);
Don't forget to release the pointer test7 filled by SHGetKnownFolderPath.
This shows the basic way to complete your task; you'll need to adapt it for your needs.
#include <ShlObj.h>
#include <strsafe.h>
void ShowTestPath()
{
PWCHAR user_dir = NULL;
WCHAR test_file_path[MAX_PATH];
if (FAILED(SHGetKnownFolderPath(FOLDERID_Profile, 0, NULL, &user_dir)))
return;
if (FAILED(StringCchCopyW(test_file_path, MAX_PATH, user_dir)))
{
CoTaskMemFree(user_dir);
return;
}
CoTaskMemFree(user_dir);
if (FAILED(StringCchCatW(test_file_path, MAX_PATH, L"\\test.exe")))
return;
MessageBoxW(NULL, test_file_path, L"WR test file", MB_OK);
}

Trying to make an XML reader work in C ++

I am trying to read some XML code from a website, and am having a bit of trouble figuring out where my errors are. Using the code from this extremely useful post, I am trying to read a file that I have saved to my desktop ("H:\MyName\Desktop\secLendingXML.cfm.xml"). The code is below:
#include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#import <msxml6.dll> rename_namespace(_T("MSXML"))
int main(/*int argc, char* argv[]*/)
{
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
try
{
MSXML::IXMLDOMDocument2Ptr xmlDoc;
hr = xmlDoc.CreateInstance(__uuidof(MSXML::DOMDocument60), NULL, CLSCTX_INPROC_SERVER);
// TODO: if (FAILED(hr))...
if (xmlDoc->load(_T("H:\MyName\Desktop\secLendingXML.cfm.xml")) != VARIANT_TRUE)
{
printf("Unable to load input.xml\n");
}
else
{
printf("XML was successfully loaded\n");
xmlDoc->setProperty("SelectionLanguage", "XPath");
MSXML::IXMLDOMNodeListPtr wheels = xmlDoc->selectNodes("/Car/Wheels/*");
printf("Car has %u wheels\n", wheels->Getlength());
MSXML::IXMLDOMNodePtr node;
node = xmlDoc->createNode(MSXML::NODE_ELEMENT, _T("Engine"), _T(""));
node->text = _T("Engine 1.0");
xmlDoc->documentElement->appendChild(node);
hr = xmlDoc->save(_T("output.xml"));
if (SUCCEEDED(hr))
printf("output.xml successfully saved\n");
}
}
catch (_com_error &e)
{
printf("ERROR: %ws\n", e.ErrorMessage());
}
CoUninitialize();
}
system("PAUSE");
return 0;
}
The message "Unable to load input.xml" always shows, so I know I'm not having an error, but that the code is unable to load my XML file.
Do I need to save the XML file in a different location? Is the ".cfm" before the ".xml" screwing with the reading process?
To give an idea of my intended direction, I want to be able to load the XML file from the New York Fed website and read it into some sort of data file where I can automate the data retrieving process. That way, whenever the website is updated, I will automatically be notified, and it will be reflected by the change in the data file that I will have saved somewhere on my computer. If anyone also wants to help with how I go about that part - poll intervals, tracking website changes, etc. - that would also be appreciated.
Thank you for any and all help.
Do you need to escape the backslashes in your path string? e.g.
xmlDoc->load(_T("H:\\MyName\\Desktop\\secLendingXML.cfm.xml")

How does _stat() under Windows exactly work

In my code I try to get the permissions for a file with _stat(). Currently I want to run it under Windows. The method is as follows:
bool CFile::Private::checkPermissions(std::string sFilename, CFile::EOpenmode iOpenmode)
{
std::string sErrMsg = "";
bool bResult = true;
struct _stat buf;
int iResult = 0;
// Get data associated with "crt_stat.c":
iResult = _stat( sFilename.c_str(), &buf );
// Check if statistics are valid:
if( iResult != 0 )
{
switch (errno)
{
case ENOENT:
sErrMsg = "File: " + sFilename + " not found.";
break;
case EINVAL:
sErrMsg = "Invalid parameter to _stat(filename, &buf).";
break;
default:
/* Should never be reached. */
sErrMsg = "Unexpected error in _stat(filename, &buf).";
}
throw std::runtime_error(sErrMsg);
}
else
{
if((iOpenmode & CFile::Read) && (!(buf.st_mode & S_IREAD)))
{
bResult = false;
}
if((iOpenmode & CFile::Write) && (!(buf.st_mode & S_IWRITE)))
{
bResult = false;
}
}
return bResult;
}
The only way to get 'false' for permission is to set the file's attribute 'read only'. Instead of this, set the security properties of the user (reject writing and reading) will get 'true' for checkPermissions(...). How to check both, the attributes and the user permissions for Windows?
Rumo
_stat is a function that is not native to Windows. It's a helper function to ease the porting of UNIX programs to Windows. But the UNIX file model just doesn't apply to Windows, so not all fields make sense. For instance, Windows has real ACL's, not rwx bits. There's just no way to fit all the ACL information in st_mode.
If you want to test ACL permissions, the proper way is to just try: call CreateFile() and check GetLastError(). Trying to get file permissions up front is not reliable as they can change at any time.
If we're talking about the same _stat() it's pretty clear from this MSDN article exactly what it does. Basically, you supply it a path to a file in question and a pointer to a _stat struct and it will dump the permissions to the struct if it returns 0.
The example C++ code in the article is pretty good.
As for testing user permissions, IsUserAnAdmin() does the job pretty well. You may be able to use this MSDN article for a different approach.
I hope this helps!