I'm trying to setup a sandbox akin to chromium. In particular, I'm trying to replicate their trick of creating a sleeping process with a low-privilege token, then setting a high-privilege token temporarily before running it. The idea is to let the process do all its initialization in high-privilege mode, then reverting to the low-privilege token right before running any unsafe code.
So far, I'm struggling just to get a basic test up and running. Here's my code:
#include "stdafx.h"
#include <atlbase.h>
#include <iostream>
#include <cassert>
#include <vector>
#include <string>
#include <AccCtrl.h>
#include <aclapi.h>
#define VERIFY(x) { bool r = x; assert(r); }
uint8_t* GetTokenInfo(const HANDLE& token, TOKEN_INFORMATION_CLASS info_class, DWORD* error)
{
// Get the required buffer size.
DWORD size = 0;
::GetTokenInformation(token, info_class, NULL, 0, &size);
if (!size)
{
*error = ::GetLastError();
return nullptr;
}
uint8_t* buffer = new uint8_t[size];
if (!::GetTokenInformation(token, info_class, buffer, size, &size))
{
*error = ::GetLastError();
return nullptr;
}
*error = ERROR_SUCCESS;
return buffer;
}
int main()
{
// Open the current token
CHandle processToken;
VERIFY(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &processToken.m_h));
// Create an impersonation token without restrictions
HANDLE impersonationToken;
VERIFY(DuplicateToken(processToken, SecurityImpersonation, &impersonationToken));
// Build the list of the deny only group SIDs
DWORD error;
uint8_t* buffer = GetTokenInfo(processToken, TokenGroups, &error);
if (!buffer) return error;
TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer);
std::vector<SID*> sids_for_deny_only;
for (unsigned int i = 0; i < token_groups->GroupCount; ++i)
{
if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 &&
(token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0)
{
sids_for_deny_only.push_back(reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
}
}
{
DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
uint8_t* buffer = new uint8_t[size];
TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(buffer);
BOOL result = ::GetTokenInformation(processToken, TokenUser, token_user, size, &size);
if (!result) return ::GetLastError();
sids_for_deny_only.push_back(reinterpret_cast<SID*>(token_user->User.Sid));
}
size_t deny_size = sids_for_deny_only.size();
SID_AND_ATTRIBUTES *deny_only_array = NULL;
if (deny_size)
{
deny_only_array = new SID_AND_ATTRIBUTES[deny_size];
for (unsigned int i = 0; i < sids_for_deny_only.size(); ++i)
{
deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY;
deny_only_array[i].Sid = const_cast<SID*>(sids_for_deny_only[i]);
}
}
// Create restricted sids
DWORD size_sid = SECURITY_MAX_SID_SIZE;
BYTE sid_[SECURITY_MAX_SID_SIZE];
VERIFY(::CreateWellKnownSid(WinNullSid, NULL, sid_, &size_sid));
SID_AND_ATTRIBUTES sidsToRestrict[] =
{
reinterpret_cast<SID*>(const_cast<BYTE*>(sid_)),
0
};
// Create the restricted token
HANDLE restrictedToken;
VERIFY(::CreateRestrictedToken(processToken,
0, // flags
deny_size,
deny_only_array,
0,
0,
_countof(sidsToRestrict), // number of SIDs to restrict,
sidsToRestrict, // no SIDs to restrict,
&restrictedToken));
VERIFY(::IsTokenRestricted(restrictedToken));
// Create a process using the restricted token (but keep it suspended)
STARTUPINFO startupInfo = { 0 };
PROCESS_INFORMATION processInfo;
VERIFY(::CreateProcessAsUser(restrictedToken,
L"C:\\Dev\\Projects\\SandboxTest\\Debug\\Naughty.exe",
0, // cmd line
0, // process attributes
0, // thread attributes
FALSE, // don't inherit handles
CREATE_SUSPENDED | DETACHED_PROCESS, // flags
0, // inherit environment
0, // inherit current directory
&startupInfo,
&processInfo));
// Set impersonation token with more rights
{
HANDLE temp_thread = processInfo.hThread;
if (!::SetThreadToken(&temp_thread, impersonationToken))
{
return 1;
}
}
// Run the process
if (!::ResumeThread(processInfo.hThread)) // Other process crashes immediately when this is run
{
return 1;
}
std::cout << "Done!" << std::endl;
return 0;
}
Not quite sure about deny list and restrict list yet, but if I understand this correctly it should be irrelevant. I'm calling SetThreadToken with my unrestricted token before running the thread, so I figure it should not matter what settings I use for restrictedToken. However, this is not the case; the new process crashes with the error code 0xc00000a5. If I use processToken instead of restrictedToken in CreateProcessAsUser, the code runs just fine. It's like SetThreadToken isn't doing anything.
I'm not doing much in naughty.exe right now, just starting an infinite loop.
Anyone know what I'm doing wrong here?
Edit 1:
According to this page, 0xc00000a5 means "STATUS_BAD_IMPERSONATION_LEVEL". Not sure on this, but I think I'm missing SeImpersonatePrivilege, causing stuff to fail. Still investigating options...
Edit 2:
Okay, seems like I had to reduce the privilege of the impersonation token to be able to use it with the other process. Not sure why, but not I can run the program without admin rights.
Still getting an error though :/ Now it's "STATUS_DLL_NOT_FOUND". Best lead from examining Process Monitor logs is an ACCESS DENIED on "C:\Windows\SysWOW64\ucrtbased.dll". The weird part is that it seems to be working once in a while (i.e. the spawned process sometimes runs just fine). Back to digging...
The problem is caused by the startup code trying to load the C runtime DLL from a new thread (which doesn't have access to the high-privilege token). What worked for me is to statically link the CRT into the sandbox process (i.e. /MTd in Debug builds, /MT in Release builds).
the new process crashes with the error code 0xc00000a5 / STATUS_BAD_IMPERSONATION_LEVEL
I've encountered this when:
the restricted token specifies SidsToRestrict that the permissive token does not.
the restricted token specifies a lower integrity level than the permissive token.
"restricted": The more restrictive token passed to CreateProcessAsUser
"permissive": The less restrictive token passed to SetThreadToken, and used until that thread calls RevertToSelf
It appears you cannot unrestrict sids or raise the integrity level with SetThreadToken, even if the parent process is unrestricted in these regards - you can only undeny sids, or unremove privileges.
I had to reduce the privilege of the impersonation token to be able to use it with the other process
This was a red herring for me. I tried every combination of keeping/removing privilige LUIDs for the permissive and restrictive tokens with little effect.
0xC0000135 / STATUS_DLL_NOT_FOUND
Best lead from examining Process Monitor logs is an ACCESS DENIED on "C:\Windows\SysWOW64\ucrtbased.dll". The weird part is that it seems to be working once in a while (i.e. the spawned process sometimes runs just fine).
The DLL loading/initializing code appears to be multithreaded. Speculating a bit here, my theory is that the new threads don't inherit the permissive token specified by SetThreadToken, and that ucrtbased.dll only loads successfully if the initial/main thread happened to be the thread that loaded ucrtbased.dll, and will fail if it's instead loaded by any of the worker threads - hence why it works sometimes, but typically not.
Workaround options:
Statically link the CRT. This is my current preference.
Don't pass Everyone or Users to SidsToDisable. Defeats the point of denying sids for sandboxing purposes in the first place, so I'd recommend against this, especially since I see no way to disable them later.
Have the parent process listen for CREATE_THREAD_DEBUG_EVENT and SetThreadToken for them too? I haven't tested this, but I suspect it might work. Would only want to do this during startup, lest you break open the sandbox after the child process has called RevertToSelf() etc.
0xC0000142 / ERROR_DLL_INIT_FAILED
Okay, this one's just me: I encountered this when trying to spawn a process at Untrusted integrity when initializing bcrypt.dll for Rust's stdlib. Spawn at Low instead, and have the child process lower itself to Untrusted post-init IMO.
How the heck do you use SidsToRestrict at all then?
You can't go from nullptr restrictions on a permissive token to real restrictions on a restricted token without causing 0xc00000a5 / STATUS_BAD_IMPERSONATION_LEVEL.
However, you can go from one restriction list to another, and neither necessairly needs to contain all the exact same SIDs as the other.
With a restrictive SidsToRestrict of only S-1-0-0 "NULL SID", I can use a permissive SidsToRestrict containing only:
S-1-1-0 "Everyone" (otherwise child dies w/ STATUS_ACCESS_DENIED)
S-1-5-5-x-yyyyyyy "LogonSessionId_..." (otherwise dies w/ STATUS_DLL_INIT_FAILED?)
Perhaps S-1-0-0 is considered a subset of Everyone, or perhaps the restricted sids can be outright disjoint?
Using all group SIDs marked SE_GROUP_ENABLED | SE_GROUP_LOGON_ID for your permissive token might be more appropriate.
Note that the child can't lower it's integrity level unless it can OpenProcessToken(.., ADJUST_DEFAULT, ..) based on the current access token.
The only overlap between the permissive token's restriction sids, and the restricted default TokenDefaultDacl, is the logon session, which doesn't grant write access by default:
ACCESS_ALLOWED_ACE { Mask: GENERIC_ALL, Sid: S-1-5-21-xxxx-yyyy-zzzz "%USERNAME%", .. }
ACCESS_ALLOWED_ACE { Mask: GENERIC_ALL, Sid: S-1-5-18 "SYSTEM", .. }
ACCESS_ALLOWED_ACE { Mask: GENERIC_READ | GENERIC_EXECUTE, Sid: S-1-5-5-x-yyyyyyyyy "LogonSessionId_x_yyyyyyyyy, .. }
So you may want to create a new default dacl for the restricted token with:
InitializeAcl(...);
AddAccessAllowedAce(acl, ACL_REVISION, TOKEN_ADJUST_DEFAULT | ..., logon_session_sid);
TOKEN_DEFAULT_DACL default_dacl = { acl };
SetTokenInformation(restriced, TokenDefaultDacl, &default_dacl, sizeof(default_dacl));
And ensure you adjust your child process's process token integrity level before calling RevertToSelf.
Related
I'm trying to use the Leadtools API version 21 for automatically scanning some documents and here is a sudo code of what I have done (it runs in a secondary thread and the unlock has been done in the main thread):
void CheckRetCode(int rc)
{
if (SUCCESS != rc)
{
L_TCHAR errMsg[1024];
memset(errMsg, 0, sizeof(errMsg));
L_GetFriendlyErrorMessage(rc, errMsg, 1024, L_FALSE);
throw TLeadException(errMsg, rc);
}
}
void OnThreadExecute(void)
{
HTWAINSESSION hSession = nullptr;
APPLICATIONDATA appData;
L_INT nRet;
L_TCHAR pszTwnSourceName[1024];
LTWAINSOURCE sInfo;
memset(&appData, 0, sizeof(APPLICATIONDATA));
appData.uStructSize = sizeof(APPLICATIONDATA);
appData.hWnd = hWnd;// hWnd is valid handle of my main window
appData.uLanguage = TWLG_ENGLISH_USA;
appData.uCountry = TWCY_USA;
wcscpy(appData.szManufacturerName, L"MyCompanyName");
wcscpy(appData.szAppProductFamily, L"MyProductName");
wcscpy(appData.szAppName, appData.szAppProductFamily);
wcscpy(appData.szVersionInfo, L"Version 0.1.0.1");
nRet = L_TwainInitSession2(&hSession, &appData, LTWAIN_INIT_MULTI_THREADED);
CheckRetCode(nRet);// the exception gets catched elsewhere but no error reported here
memset(pszTwnSourceName, 0, sizeof(pszTwnSourceName));
wcscpy(pszTwnSourceName, L"EPSON Artisan837/PX830"); // the name of the scanner is verifyed
sInfo.uStructSize = sizeof(LTWAINSOURCE);
sInfo.pszTwainSourceName = pszTwnSourceName;
CheckRetCode(L_TwainSelectSource(hSession, &sInfo)); // No error reported here
CheckRetCode(L_TwainStartCapsNeg(hSession)); // in here I get the return value -84 which is reported as "TWAIN DS or DSM reported error, app shouldn't (no need for your app to report the error)."
// the rest of the code but we cannot get there since above code reports error
}
Can anyone tell me what I'm doing wrong? Is there a step that I'm missing here?
EditThe function L_TwainSelectSource() make no effort to make sure the supplied source is valid and does not even return an error. As result, if you set the selected source to a garbage name, it will act as if it accepted it. From that point on if you try to Get/Set anything or try to acquire an image, every function returns -84.
Thank you
Sam
To test your code, I put the main window’s handle in a global variable:
globalhWnd = hWnd;
And modified your function to use that handle like this:
void OnThreadExecute(void *)
{
...
appData.hWnd = globalhWnd; // hWnd is valid handle of my main window
...
}
Then created a thread for it from the main program like this:
globalhWnd = hWnd;
_beginthread(OnThreadExecute, 0, 0);
I tried this with 5 different Twain sources: 2 virtual and 3 physical scanners (one of them an old Epson). All 5 drivers returned SUCCESS when calling L_TwainStartCapsNeg() from within the thread.
Two possibilities come to mind:
The problem might be caused by something else in your code other than the thread function.
Or the problem could be specific to your Twain driver.
To rule out the first possibility, I suggest creating a small test project that only creates a similar thread and does nothing else and trying it with different scanners. If it causes the same problem with all scanners, send that test project (not your full application) to support#leadtools.com and our support engineers with test it for you.
If the problem only happens with a specific Twain driver, try contacting the scanner’s vendor to see if they have an updated driver.
I have a dilemma. My GUI-based C++ app requires to implement drag-and-drop functionality. At the same time, I'm converting this Win32 app to UWP to submit to Windows Store. But there's one issue:
To implement drag-and-drop I need to call these two methods:
OleInitialize(NULL);
//...
HRESULT hr = RegisterDragDrop(hMainWnd, pDropTarget);
and to init WinRT stuff to work with Windows Store, I need to call:
HRESULT hr = RoInitialize(RO_INIT_MULTITHREADED);
Unfortunately OleInitialize initialized COM as single-thread apartment and RoInitialize requires multi-threaded model, while RegisterDragDrop cannot function without calling OleInitialize.
Any idea how to resolve it? (apart from moving RoInitialize and all WinRT code into a worker thread, that will complicate things.)
Raymond Chen in his usual condescending way is pretty good at criticizing things but offers no fix to an existing problem. I'm posting this mostly for later self-reference and in case someone else stumbles upon the same issue as well. I just spent several days trying to resolve this bug, so maybe it will save time for someone else.
Problem
First off, this is a native Win32 code (no .NET or C++/CX.) It is C++ with a sprinkle of WRL for easier handling of WinRT/COM stuff.
In my case I have a Win32 GUI app that implements drag-and-drop of files into its main window. So to init it, one needs to do this from the main thread, right when the app starts:
OleInitialize(NULL);
//...
HRESULT hr = RegisterDragDrop(hMainWnd, pDropTarget);
The OleInitialize call above will initialize COM for the main thread to use single-thread apartment, which is required for RegisterDragDrop to succeed. Without it, the drag-and-drop function will not work.
Then, say you decide to convert this Win32 app to UWP using Microsoft's Project Centennial converter for inclusion into Windows 10 store.
When the app is converted and listed in the store under their trial-license scheme, you will employ the following logic to check if the user has a trial or an activated (i.e. purchased) copy of the app. You'll begin it as such:
//Init COM for WinRT
RoInitialize(RO_INIT_MULTITHREADED);
ComPtr<IStoreContextStatics> pStoreContextStatics;
if(SUCCEEDED(RoGetActivationFactory(
HStringReference(L"Windows.Services.Store.StoreContext").Get(),
__uuidof(pStoreContextStatics),
&pStoreContextStatics)) &&
pStoreContextStatics)
{
//Get store context for the app
ComPtr<IStoreContext> pStoreContext;
if(SUCCEEDED(pStoreContextStatics->GetDefault(&pStoreContext)) &&
pStoreContext)
{
//Got store context
//....
}
}
and then if you need to know trial vs. activated status of the app, using this logic, you'd call:
ComPtr<IAsyncOperation<StoreAppLicense*>> p_opAppLic;
if(SUCCEEDED(pStoreContext->GetAppLicenseAsync(p_opAppLic)) &&
p_opAppLic)
{
ComPtr<IAsyncOperationCompletedHandler<StoreAppLicense*>> p_onAppLicCallback =
Callback<Implements<RuntimeClassFlags<ClassicCom>, IAsyncOperationCompletedHandler<StoreAppLicense*>, FtmBase>>(
[](IAsyncOperation<StoreAppLicense*>* pOp, AsyncStatus status)
{
if (status == AsyncStatus::Completed)
{
ComPtr<IStoreAppLicense> pAppLicResult;
if(SUCCEEDED(pOp->GetResults(&pAppLicResult)) &&
pAppLicResult)
{
BYTE nActive = -1;
BYTE nTrial = -1;
pAppLicResult->get_IsActive(&nActive);
pAppLicResult->get_IsTrial(&nTrial);
//Get app's store ID with SKU
HString strStoreId;
pAppLicResult->get_SkuStoreId(strStoreId.GetAddressOf());
if(nActive == 1 &&
nTrial == 0)
{
//Activated, or purchased copy
}
else if(nActive == 1 &&
nTrial == 1)
{
//Trial copy
}
else
{
//Error -- store returned some gibberish
}
}
}
return S_OK;
});
if(SUCCEEDED(p_opAppLic->put_Completed(p_onAppLicCallback.Get())))
{
//Success initiating async call
}
}
So, if you do all this, your UWP-converted app will behave in a very strange way. Here's an example. Say a user purchases a license for the app thru Windows Store. In turn your app logic calls the code above to see if the app is activated, but what you get back is nActive=0 and nTrial=1. Then if you check strStoreId it will be your app store ID but without the SKU. WTF!?
I know, it's really confusing. As an aside, let me explain. When you first list your app in a Windows Store it will be assigned a Store ID. Something like: ABCDEFG12345. Then if you submit any follow-up update(s) to the first version of the same app, they will add a SKU number to it, that will make the whole app ID change to ABCDEFG12345/0010, then ABCDEFG12345/0011 for the next update, and so on.
Well, the WinRT code above would return my app store ID as ABCDEFG12345 without any SKU attached to it. Which was wrong, since it was a third or so update to the first version of the app. And thus any additional attributes for that app store ID were also wrong.
So that was the issue that I was faced with...
Cause
All the headache that I described above was caused by my omission to check the result code returned from the first RoInitialize call. I would be able to catch the problem much faster if I did this:
//Init COM for WinRT
if(FAILED(RoInitialize(RO_INIT_MULTITHREADED)))
{
//WinRT COM initialization failed
//Go scratch your head why....
}
In this case RoInitialize will fail with error code RPC_E_CHANGED_MODE. The documentation for it is as helpful as Windows Help (F1) option:
A previous call to RoInitialize specified the concurrency model for
this thread as multithread apartment (MTA). This could also indicate
that a change from neutral-threaded apartment to single-threaded
apartment has occurred.
What previous call? The only parameter anyone can call it with is RO_INIT_MULTITHREADED.
So I started digging deeper and by the process of elimination found that the OleInitialize call earlier was the reason why RoInitialize failed and caused the cascade of events that I described above.
Thus I was at the point of asking the question here.
Note on the side, that the bug ridden WinRT library (ref1, ref2, ref3, ref4, ref5) gave me no indications of a problem in all the calls following RoInitialize and somewhere internally silently failed to retrieve the app's SKU because of a single-thread apartment COM initialization.
Hack/Workaround
As was suggested by RbMm in the comments above, doing the following will work, but is a totally undocumented behavior:
if(SUCCEEDED(OleInitialize(0))
{
CoUninitialize();
}
CoInitializeEx(NULL, COINIT_MULTITHREADED);
So if you don't want your app to start crashing for no apparent reason, I would not use it.
Solution
My solution that I went with was to move all the WinRT COM stuff (code I listed above: 2nd and 3rd code segments) into a separate worker thread. It will work fine from there. The issue is marshalling calls between your main thread and this worker thread. It is doable, but requires some work, i.e. using mutexes and events for synchronized access, etc.
So if anyone finds an easier fix for this, please post your solution. I'll mark it as the answer.
solution to the IDsObjPicker crashes I mentioned in the comment ealier, quick code I wrote just now.
Use code below as:
TDsObjPicker lv_PickInfo;
memset(&lv_PickInfo, 0, sizeof(TDsObjPicker));
Sec_InitDsObjPicker(&lv_PickInfo, &lv_InitInfo);
Sec_InvokeDsObjPicker(&lv_PickInfo, 0, &lv_oData);
Solution is to run the dialog in another thread and init the thread without the Ole+Com combination:
// command codes
#define DSOPCMD_EXITTHREAD 1
#define DSOPCMD_INITIALIZE 2
#define DSOPCMD_INVOKE 3
// parameters of object picker via thread
typedef struct tagDsObjPicker
{
// thread handle
HANDLE hThread;
// events
HANDLE hCmdEvt;
HANDLE hRdyEvt;
// commands
UINT CmdCode;
HRESULT hResult;
// command parameters - DSOPCMD_INITIALIZE
DSOP_INIT_INFO *InitInfo;
// command parameters - DSOPCMD_INVOKE
HWND hWnd;
IDataObject **oData;
//
} TDsObjPicker;
DWORD CALLBACK _Sec_DsObjPickerThread(VOID *in_Param)
{
/* locals */
HRESULT lv_hCreateResult;
HRESULT lv_hResult;
TDsObjPicker *lv_PickInfo;
IDsObjectPicker *lv_oPicker;
// get info structure
lv_PickInfo = (TDsObjPicker*)in_Param;
// init COM
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// preclear object pointer
lv_oPicker = NULL;
// create instance of picker
lv_hCreateResult = CoCreateInstance(
CLSID_DsObjectPicker, NULL, CLSCTX_INPROC_SERVER,
IID_IDsObjectPicker, (VOID**)&lv_oPicker);
// while thread is not aborted
while (lv_PickInfo->CmdCode != DSOPCMD_EXITTHREAD)
{
// wait for command event
if (WaitForSingleObject(lv_PickInfo->hCmdEvt, INFINITE) == 0)
{
// what command?
switch (lv_PickInfo->CmdCode)
{
// call init
case DSOPCMD_INITIALIZE:
{
// call object
if (lv_hCreateResult)
lv_hResult = lv_hCreateResult;
else
lv_hResult = lv_oPicker->Initialize(lv_PickInfo->InitInfo);
// done
break;
}
// call invoke
case DSOPCMD_INVOKE:
{
// call object
if (lv_hCreateResult)
lv_hResult = lv_hCreateResult;
else
lv_hResult = lv_oPicker->InvokeDialog(lv_PickInfo->hWnd, lv_PickInfo->oData);
// done
break;
}
// other command codes
default:
lv_hResult = E_FAIL;
break;
}
// store result
lv_PickInfo->hResult = lv_hResult;
// notify caller
SetEvent(lv_PickInfo->hRdyEvt);
}
}
// destroy the picker object
if (lv_oPicker)
lv_oPicker->Release();
// cleanup COM
CoUninitialize();
// leave the thread
return 0;
}
VOID Sec_DoneDsObjPicker(TDsObjPicker *in_PickInfo)
{
// is thread created?
if (in_PickInfo->hThread)
{
// set command code
in_PickInfo->CmdCode = DSOPCMD_EXITTHREAD;
// trigger the thread to process the code
SetEvent(in_PickInfo->hCmdEvt);
// wait for thread to finish
WaitForSingleObject(in_PickInfo->hThread, INFINITE);
// close thread handle
CloseHandle(in_PickInfo->hThread);
}
// close event handles
if (in_PickInfo->hCmdEvt) CloseHandle(in_PickInfo->hCmdEvt);
if (in_PickInfo->hRdyEvt) CloseHandle(in_PickInfo->hRdyEvt);
// clear
memset(in_PickInfo, 0, sizeof(TDsObjPicker));
}
HRESULT Sec_InitDsObjPicker(TDsObjPicker *in_PickInfo, DSOP_INIT_INFO *in_InitInfo)
{
/* locals */
DWORD lv_TID;
// thread not yet created?
if (!in_PickInfo->hThread)
{
// create events
in_PickInfo->hCmdEvt = CreateEvent(0,0,0,0);
in_PickInfo->hRdyEvt = CreateEvent(0,0,0,0);
// if ok
if (in_PickInfo->hCmdEvt && in_PickInfo->hRdyEvt)
{
// create the thread
in_PickInfo->hThread = CreateThread(
0, 0, _Sec_DsObjPickerThread, in_PickInfo, 0, &lv_TID);
}
// failed?
if (!in_PickInfo->hThread)
{
// cleanup
Sec_DoneDsObjPicker(in_PickInfo);
// return with error
return E_OUTOFMEMORY;
}
}
// store parameter
in_PickInfo->InitInfo = in_InitInfo;
// set command code
in_PickInfo->CmdCode = DSOPCMD_INITIALIZE;
// trigger the thread to process the code
SetEvent(in_PickInfo->hCmdEvt);
// wait for result
WaitForSingleObject(in_PickInfo->hRdyEvt, INFINITE);
// return the result
return in_PickInfo->hResult;
}
HRESULT Sec_InvokeDsObjPicker(TDsObjPicker *in_PickInfo, HWND in_hWnd, IDataObject **out_oData)
{
/* locals */
MSG lv_Msg;
// thread not yet created?
if (!in_PickInfo->hThread)
return E_FAIL;
// store parameters
in_PickInfo->hWnd = in_hWnd;
in_PickInfo->oData = out_oData;
// set command
in_PickInfo->CmdCode = DSOPCMD_INVOKE;
// trigger the thread
SetEvent(in_PickInfo->hCmdEvt);
// process messages of this thread while picker runs in other thread until event
while (MsgWaitForMultipleObjects(1, &in_PickInfo->hRdyEvt, 0, INFINITE, QS_ALLINPUT) != 0)
{
// get next message
while (PeekMessage(&lv_Msg, 0,0,0, PM_REMOVE))
{
// translate/dispatch the message
TranslateMessage(&lv_Msg);
DispatchMessage(&lv_Msg);
}
}
// return the result
return in_PickInfo->hResult;
}
You asked why calling OleInitialize() first, followed by CoUnintialize and then reinit COM via CoInitializeEx works and is safe, look at the code of the rewritten OLE server in WINE, https://github.com/wine-mirror/wine/blob/master/dlls/ole32/ole2.c it comes pretty close to the "real thing". The OleInitialize calls CoInitializeEx itself with COINIT_APARTMENTTHREADED and fails before doing the OLE-specific initializations upon a fail of CoInitializeEx. There is no reason to fail as the OLE code can run as well in MULTITHREADED mode. Remember MULTITHREADED means the caller must take care of synchronisation/locking while with APARTMENTTHREADED the COM libray will handle it for the code. So if you make sure you do not call the OLE code like dragdrop and clipboard at the same time from multiple threads then there is no problem. Keeping all UI in the main thread will do that. As you should already write multithreaded-aware code yourself using the requested MULTITHREADED mode.
I have the problem with directshow filters/drivers which lock the process when COM is initialized with APARTMENTTHREADED even when directshow is called from a thread with THREADED while the main UI thread runs in APARTMENTTHREADED.
Uninitializing COM after initializing OLE, then re-inititializing COM with MULTITHREAED during startup in the main UI thread makes you bypass the failure in OleInitialize. It is the best solution to make sure all runs well.
I'm running a Visual C++ MFC application in release mode. I'm compiling everything using Visual Studio 2010.
My app runs a mini CNC mill through USB VCP communication.
I have a XML file that stores the app's settings.
My problem is this: ocassionaly (and this is repeatable) the pointer to the tinyxml2::XMLDocument I'm using gets set to 0x000.
Info:
Occasionally, the XML file get written to while the mill is running.
Before the error happens, the mill I'm running siezes for almost 30 seconds.
I'm using mutex locks to make sure the xmldoc doesn't get written to file twice at once.
The mutex locks are working, and the mutex error never occurs. I know the mutex code isn't perfect, but that isn't the issue. Honest.
I never write to the xmldoc pointer except when the parent class is booting up.
And then, all of a sudden, the xmlDoc pointer gets set to zero.
Any thoughts anyone?
Here is my saving code, although the problem may lie elsewhere:
void XMLSettings::SaveToXML()
{
HANDLE g_Mutex = CreateMutex( NULL, TRUE, "XMLSavingMutex");
DWORD wait_success = WaitForSingleObject( g_Mutex, 30000L);
if(wait_success == WAIT_OBJECT_0){
CIsoProApp* pApp = (CIsoProApp*)AfxGetApp();
if(PathFileExists(pApp->DrivePath + "IsoPro\\temp.xml"))
{
DeleteFile(pApp->DrivePath + "IsoPro\\temp.xml");
}
if(0==&xmlDoc)
{
OutputDebugString("xmlDoc == NULL");
}
int errorcode = xmlDoc->SaveFile(pApp->DrivePath + "IsoPro\\temp.xml");
if(errorcode != 0)
{
OutputDebugString("xmlDoc == errorcode");
}
if(0==&xmlDoc)
{
OutputDebugString("xmlDoc == NULL2");
}
if(0==xmlDoc)
{
OutputDebugString("xmlDoc == NULL");
}
if(PathFileExists(pApp->DrivePath + "IsoPro\\Settings.xml"))
{
DeleteFile(pApp->DrivePath + "IsoPro\\Settings.xml");
}
MoveFile(pApp->DrivePath + "IsoPro\\temp.xml",pApp->DrivePath + "IsoPro\\Settings.xml");
ReleaseMutex(g_Mutex);
}
else
{
int errorInt = GetLastError();
CString error;
error.Format("%d",errorInt);
if(errorInt != ERROR_ALREADY_EXISTS)
{
AfxMessageBox("XMLSavingMutex Error. WaitSuccess = " + wait_success);
AfxMessageBox("XMLSavingMutex Error. GetLastError = " + error);
}
}
CloseHandle(g_Mutex);
}
Since it seems that you are creating a Mutex each time SaveToXML is called, you should change your call to
HANDLE g_Mutex = CreateMutex( NULL, FALSE, "XMLSavingMutex");
Doing this will create a named mutex that allows the implementation to dictate who the owner is; other threads will receive the same mutex.
From the doc:
Two or more processes can call CreateMutex to create the same named mutex. The first process actually creates the mutex, and subsequent processes with sufficient access rights simply open a handle to the existing mutex. This enables multiple processes to get handles of the same mutex, while relieving the user of the responsibility of ensuring that the creating process is started first. When using this technique, you should set the bInitialOwner flag to FALSE; otherwise, it can be difficult to be certain which process has initial ownership.
(Credit to WhozCraig for pointing out named mutexes)
It appears that I was accessing the xml getter while writing the xml to a file. I put a single mutex lock in place for all xml actions and things seem to be functioning properly. Thanks to everyone for their help. I'll be in touch with more info if it becomes available.
If the parent process has used LogonUser so that the access token being used for file access is different than than the token the process was started with, how can the DLL find out the NT User name that the file accesses will be processed under?
If I had a specific file location then I could use GetFileSecurity, however I don't know any guaranteed accessible paths in the context of the DLL.
If I used the following:
PSID ownedSID(NULL);
SECURITY_INFORMATION siRequested = OWNER_SECURITY_INFORMATION;
wSecInfoOK = GetSecurityInfo(GetCurrentProcess(), SE_KERNEL_OBJECT, siRequested, &ownedSID, NULL, NULL, NULL, NULL);
then the PSID returned references the Windows user of the logged on process rather than that under which any writes will be treated as!
New Question in light comment / answer from #arx
I am now using TokenUser with GetTokenInformation on the handle from OpenThreadToken, but again I am getting the launching user but not the impersonated user
HANDLE hThreadToken = NULL;
if (OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hThreadToken))
{
// success
CHeapPtr<TOKEN_USER, CGlobalAllocator> pToken;
DWORD length = 0U, dwError(0UL);
if (!GetTokenInformation(hThreadToken, TokenUser, NULL, 0, &length) && ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
pToken.AllocateBytes(length);
SetLastError(ERROR_SUCCESS);//Reset last error - we have now allocated the required memory so the buffer is now big enough i.e GetLastError() != ERROR_INSUFFICIENT_BUFFER
if (pToken && GetTokenInformation(hThreadToken, TokenUser, pToken, length, &length))
{
if (IsValidSid(pToken->User.Sid))
sFailedUser = WinSecurityInfo::GetAccountSID(pToken->User.Sid, dwError);
}
dwError = GetLastError();
if (dwError)
{
boost::system::error_code sidError = MakeSysError(dwError);
TRACE("Error text for GetLastError() = '%s'\n", sidError.message().c_str());
}
}
}
P.S WinSecurityInfo::GetAccountSID is just a wrapper around LookupAccountSid
P.P.S Tried both FALSE and TRUE in OpenThreadToken, no change
You are looking at the wrong information in the thread token retrieved with OpenThreadToken. To get the identity of the user being impersonated you need to look at the TokenUser, not the TokenOwner.
Use GetTokenInformation to retrieve the user.
However, rather than going to great lengths to work in the face of impersonation, it is more usual to specify as part of your API contract that you don't. And then ignore the problem.
I want to close a handle to a mutex located in another process, so I can run more than one instance of the application.
I already know this can be done, see Process Explorer. Example: Windows Minesweeper (Windows 7) uses a mutex to only allow one game, so I thought I would use it as an example since it's pre-installed with Windows and therefore easier for you guys to guide me.
The mutex that I need to close is \Sessions\1\BaseNamedObjects\Oberon_Minesweeper_Singleton, which I found using Process Explorer.
After closing this mutex I was able to launch two games of Minesweeper, but I want to do this in my program using C++.
After some searching I have found that I might need the API DuplicateHandle. So far I haven't been able to close the handle on this mutex.
Here is my code so far:
#include <Windows.h>
#include <iostream>
using namespace std;
void printerror(LPSTR location){
printf("Error: %s_%d", location, GetLastError());
cin.get();
}
int main(){
DWORD pid = 0;
HWND hMineWnd = FindWindow("Minesweeper", "Minesveiper");
GetWindowThreadProcessId(hMineWnd, &pid);
HANDLE hProc =OpenProcess(PROCESS_DUP_HANDLE, 0, pid);
if(hProc == NULL){
printerror("1");
return 1;
}
HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, "Oberon_Minesweeper_Singleton");
if(hMutex == NULL){
printerror("2");
return 2;
}
if(DuplicateHandle(hProc, hMutex, NULL, 0, 0, FALSE, DUPLICATE_CLOSE_SOURCE) == 0){
printerror("3");
return 3;
}
if(CloseHandle(hMutex) == 0){
printerror("4");
return 4;
}
return 0;
}
This code returns 0, but the mutex is still there, and I am not able to launch more games of Minesweeper. I think some of my parameters to DuplicateHandle are wrong.
The second argument to DuplicateHandle expects "an open object handle that is valid in the context of the source process", however I believe the handle you're passing in would only be valid within the current process (OpenMutex creates a new handle to an existing mutex object). You'll likely need to determine what the mutex's handle is in the remote process, and use that value when calling DuplicateHandle.