How to run ShellExecute on shell object clsid? - windows

Shell explorer on Windows define global objects such as ::{2559a1f4-21d7-11d4-bdaf-00c04f60b9f0 which is a link to the user "internet browser". I got these value from a shortcut that is drag&dropped over my application, but I can't find how to use it to open it...
I'd like to get information about what this object is too.
Does someone know how to ask Windows about what this object is ?
Does someone know how to open it ?
I have found that these objects can be found by reading from the registry at :
initString = HLCR\CLSID\\{CLSID\}\PersistentBag\InitString
openCommand = HKLM\Software\Clients\{initString}\shell\open\command
Usually its not a good idea to read directly from the registry. I'd be please to have a cleaner way to read these values.
Thanks.

Apparently you need to fill in a SHELLEXECUTEINFO structure, with the fMask field set to SEE_MASK_CLASSKEY, the lpVerb field set to "open" and the lpClass set to your GUID.
However, I never got the damn thing to work. Instead I used a file of the correct type to use in a CreateProcess call.
HINSTANCE h = FindExecutable("blank.htm", NULL, buffer);
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
char url[MAX_PATH];
sprintf(url, "\"%s\" %s", buffer, (LPCTSTR)m_url);
CreateProcess(NULL, url, NULL, NULL, FALSE, 0 , NULL, NULL, &si, &pi);
Which opens the app that is defined as the default viewer for files of type .htm.

From "Creating Shell Extensions with Shell Instance Objects" at http://msdn.microsoft.com/en-us/library/ms997573.aspx it is told that
Whereas a traditional shell extension requires a DLL to implement the object, a shell instance object retrieves everything it needs to know from the registry.
It seems to be safe to read the informations from the registry as it is the documented solution to create them.

Related

Get IShellItem for Quick Access?

This failes with 0x80070057:
SHCreateItemFromParsingName(L"::{679F85CB-0220-4080-B29B-5540CC05AAB6}", NULL, IID_PPV_ARGS(&psi))
While this works for "My Computer":
SHCreateItemFromParsingName(L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", NULL, IID_PPV_ARGS(&psi))
Am I using the wrong GUID for Quick Access?
Note this GUID (CLSID_HomeFolder) is not officially documented.
You must use this type of parsing name instead for it to work (shell: is a url scheme):
SHCreateItemFromParsingName(L"shell:::{679F85CB-0220-4080-B29B-5540CC05AAB6}", NULL, IID_PPV_ARGS(&psi));

Replacing ".jpg" thumbnail handler

I wrote a new thumbnail handler for windows 10. When I apply it on my custom ".mic" files (which are renamed ".jpg" files) the handler works well. However, for some reason the handler is not being called at all for ".jpg" files.
These are the entries I add to the registry during "DllRegisterServer":
// List of registry entries we want to create
const REGISTRY_ENTRY rgRegistryEntries[] =
{
{ HKEY_LOCAL_MACHINE, L"Software\\Classes\\CLSID\\" SZ_CLSID_MYTHUMBHANDLER, REG_SZ, NULL, SZ_XDRVTHUMBHANDLER },
{ HKEY_LOCAL_MACHINE, L"Software\\Classes\\CLSID\\" SZ_CLSID_MYTHUMBHANDLER L"\\InProcServer32", REG_SZ, NULL, szModuleName },
{ HKEY_LOCAL_MACHINE, L"Software\\Classes\\CLSID\\" SZ_CLSID_MYTHUMBHANDLER L"\\InProcServer32", REG_SZ, L"ThreadingModel", L"Apartment" },
{ HKEY_LOCAL_MACHINE, L"Software\\Classes\\.mic\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}" , REG_SZ, NULL, SZ_CLSID_MYTHUMBHANDLER },
{ HKEY_LOCAL_MACHINE, L"Software\\Classes\\.jpg\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}" , REG_SZ, NULL, SZ_CLSID_MYTHUMBHANDLER },
};
hr = S_OK;
for (int i = 0; i < ARRAYSIZE(rgRegistryEntries) && SUCCEEDED(hr); i++)
{
hr = CreateRegKeyAndSetValue(&rgRegistryEntries[i]);
}
When I tried testing my handler with the "ThumbnailProvider.exe" utility (provided with windows SDK), it works well. I ran the utility with a ".jpg" file, and my handler was successfully called.
Why is my handler not being called by explorer?
EDIT:
On some other PC, it seems to work perfectly. My original registration code works well and my handler is being called for "jpg" files. Both PCs have windows 10, and in fact they are VMs using the same base image.
EDIT:
I noticed that my handler is being called for some ".jpg" files, on others it doesn't. I may be wrong here, but the major difference that I have noticed between the files is the "Resolution Unit" property. For the working files there was no value for this property, for the non-working files, the value for this property is "2". I get the same behavior on all my PCs.
Any idea how this property affects the thumbnail handler being called? If it is not the "Resolution Unit" property, what can be the difference which causes it?
MSDN says that you need to call SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0) when you register a new extension handler object.
If you do not call SHChangeNotify, the change might not be recognized
until the system is rebooted.
On my machine the Microsoft handler is registered as a generic handler under HKLM\SOFTWARE\Classes\SystemFileAssociations\image and I believe that key is supposed to have a lower priority but it might be worth investigating. As noted in the comments by Simon, the shell reads from HKCR and that is a combined view where HKEY_CURRENT_USER can override HKEY_LOCAL_MACHINE so you should also make sure nothing is set for your specific user.
Going into semi-documented territory we see that Explorer may cache some CLSID information under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Cached and HKCU\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Cached so if all else fails you could try to delete these keys (at least the one under HKCU should be safe to delete).
I think I found the reason why my thumbnail handler is not being called on some of the ".jpg" files. It seems that some jpg images have an embedded thumbnail inside them. So for these files no thumbnail handler is being called at all.
I used exiftool utility to read the image properties, in the output I see the following lines:
Photoshop Thumbnail : (Binary data 8015 bytes, use -b option to extract)
Thumbnail Image : (Binary data 8015 bytes, use -b option to extract)
I used exiftool to remove the thumbnails from the file. Once I did that, my handler was successfully called for this image.

Windows-10 CreateFile2 error (ERROR_NOT_SUPPORTED_IN_APPCONTAINER)

CreateFile2 api is returning ERROR_NOT_SUPPORTED_IN_APPCONTAINER when the file is not present/available in the path. My code is as below
CREATEFILE2_EXTENDED_PARAMETERS ms_param = {0};
ms_param.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
ms_param.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
ms_param.dwFileFlags = FILE_FLAG_NO_BUFFERING;
ms_param.dwSecurityQosFlags = SECURITY_DELEGATION;
ms_param.lpSecurityAttributes = NULL;
ms_param.hTemplateFile = NULL;
g_hfile = CreateFile2(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, OPEN_EXISTING, &ms_param);
if (g_hfile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
I have already looked into this thread:CreateFile2 error in WinRT project (ERROR_NOT_SUPPORTED_IN_APPCONTAINER) , reporting similar issue. The solution suggested there doesnt work for me.
From this msdn page:CreateFile2 If the specified file or device does not exist, the function fails and the last-error code is set to ERROR_FILE_NOT_FOUND (2).But I am getting ERROR_NOT_SUPPORTED_IN_APPCONTAINER error
#Hans Passant's, comment helped me figure out the answer. Since I cannot upvote the comment I am adding it as an answer here:
CreateFile2 is not a way to bypass the sandbox restrictions. You only get access to the directories that the appxmanifest asks permissions for.
After checking his comment, I debugged my app deeper & was able to see that under certain scenarios, app was trying to read/write to files which were outside of accessible directories.

SecTrustedApplicationCreateFromPath being too smart?

I've got an application that also configures and runs a daemon. I am trying to give both the daemon and the application access permissions to the keychain item. The basic code:
SecKeychainItemRef item;
// create a generic password item
SecTrustedApplicationRef appRef[2];
SecAccessRef ref;
SecTrustedApplicationCreateFromPath( NULL, &appRef[0] );
SecTrustedApplicationCreateFromPath( DAEMON_PATH, &appRef[1] );
CFArrayRef trustList = CFArrayCreate( NULL, ( void *)appRef, sizeof(appRef)/sizeof(*appRef), NULL );
SecAccessCreate( descriptor, trustList, &ref );
SecKeychainItemSetAccess( item, ref );
The keychain entry is created, however the only application listed in the Keychain Access tool as always having access is the main application. Let's call it FOO.app. DAEMON_PATH points to the absolute path of the daemon which is in the application bundle -- call it FOO.daemon.
If I manually go within Keychain Access and select the daemon, it does get added to the list.
Any idea on how to get SecTrustedApplicationCreateFromPath to honor the full/absolute path?
If you need an answer today...
I tried to replace access object for existing keychain item with no success, too. So, I decided to modify existing access object rather than replace it, and this approach works well.
The following pseudocode demonstrates the idea. Declarations, CFRelease()s and error checking are stripped for clarity sake.
SecKeychainItemCopyAccess(item, &accessObj);
SecAccessCopySelectedACLList(accessObj, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList);
assert(CFArrayGetCount(aclList) == 1);
acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
SecACLCopySimpleContents(acl, &appList, &desc, &prompt_selector);
SecTrustedApplicationCreateFromPath(MY_APP_PATH, &app);
newAppList = CFArrayCreate(NULL, (const void**)&app, 1, NULL);
SecACLSetSimpleContents(acl, newAppList, desc, &psel);
SecKeychainItemSetAccess(item, accessObj);
I used SecAccessCopySelectedACLList to search for an ACL object with an appropriate authorization tag. You may require some other way for ACL filtering.
The straightforward access object creation must be more tricky, you are to create the same ACL structure as Keychain Access app does, rather than use default SecAccessCreate()'s ACLs. I couldn't cope with that way.

Obtaining admin privileges to delete files using rm from a Cocoa app

I am making a small app that deletes log files. I am using an NSTask instance which runs rm and srm (secure rm) to delete files.
I want to be able to delete files in:
/Library/Logs
~/Library/Logs
The issue is that the user account does not have permissions to access some files in the system library folder, such as the Adobe logs subfolder and others. For example, only the "system" user (group?) has r/w permissions for the Adobe logs folder and its contents, and the current user doesn't even have an entry in the permissions shown in the Get Info window for the folder.
What I want to be able to do exactly:
Obtain admin privileges.
Store the password in the Keychain so the app doesn't have to nag the user each time (Is the storage of the password a bad idea? Is it possible?)
Delete a file whatever the file permissions may be.
I am using NSTask because it offers notifications for task completion, getting text output from the task itself, etc. Would I need to use something else? If so, how could I replicate NSTask's completion notifications and output file handle while running rm and srm with admin privileges?
I am looking for the most secure way to handle the situation. i.e. I don't want my application to become a doorway for privilege escalation attacks.
I looked at the Authorization Services Programming Guide but I am not sure which case fits. At first I thought that AuthorizationExecuteWithPrivileges would be a good idea but after reading more on the subject it looks like this method is not recommended for security reasons.
A detailed answer would be very welcome. I'm sure some of you already had to do something similar and have some code and knowledge to share.
Thanks in advance!
Update:
I am now able to make the authentication dialog pop up and obtain privileges, like so:
OSStatus status;
AuthorizationRef authRef;
status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authRef);
AuthorizationRights authRights;
AuthorizationItem authItems[1];
authItems[0].name = kAuthorizationRightExecute;
authRights.count = sizeof(authItems) / sizeof(authItems[0]);
authRights.items = authItems;
AuthorizationFlags authFlags = kAuthorizationFlagDefaults | kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed;
status = AuthorizationCopyRights(authRef, &authRights, kAuthorizationEmptyEnvironment, authFlags, NULL);
From the looks of it, it seems that the "Factored Application" method looks the most appropriate. The thing is that, to me, rm already seems like an external helper tool. I'm not sure I get the setuid alternative suggested in the documentation. Could I set the setuid bit on rm and run it using the NSTask method I already implemented? This would mean that I wouldn't need to create my own helper tool. Could somebody elaborate on this subject?
I also looked at the BetterAuthorizationSample which is suggested as a more secure and recent alternative to the setuid bit method, but found it awfully complex for such as simple behavior. Any hints?
Thanks in advance for any help!
Perhaps a tad late, but this might be useful for future reference for other people. Most of the code is from this person.
Basically, it has a lot to do with Authorization on the Mac. You can read more about that here and here.
The code, which uses the rm tool:
+ (BOOL)removeFileWithElevatedPrivilegesFromLocation:(NSString *)location
{
// Create authorization reference
OSStatus status;
AuthorizationRef authorizationRef;
// AuthorizationCreate and pass NULL as the initial
// AuthorizationRights set so that the AuthorizationRef gets created
// successfully, and then later call AuthorizationCopyRights to
// determine or extend the allowable rights.
// http://developer.apple.com/qa/qa2001/qa1172.html
status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);
if (status != errAuthorizationSuccess)
{
NSLog(#"Error Creating Initial Authorization: %d", status);
return NO;
}
// kAuthorizationRightExecute == "system.privilege.admin"
AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0};
AuthorizationRights rights = {1, &right};
AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
// Call AuthorizationCopyRights to determine or extend the allowable rights.
status = AuthorizationCopyRights(authorizationRef, &rights, NULL, flags, NULL);
if (status != errAuthorizationSuccess)
{
NSLog(#"Copy Rights Unsuccessful: %d", status);
return NO;
}
// use rm tool with -rf
char *tool = "/bin/rm";
char *args[] = {"-rf", (char *)[location UTF8String], NULL};
FILE *pipe = NULL;
status = AuthorizationExecuteWithPrivileges(authorizationRef, tool, kAuthorizationFlagDefaults, args, &pipe);
if (status != errAuthorizationSuccess)
{
NSLog(#"Error: %d", status);
return NO;
}
// The only way to guarantee that a credential acquired when you
// request a right is not shared with other authorization instances is
// to destroy the credential. To do so, call the AuthorizationFree
// function with the flag kAuthorizationFlagDestroyRights.
// http://developer.apple.com/documentation/Security/Conceptual/authorization_concepts/02authconcepts/chapter_2_section_7.html
status = AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights);
return YES;
}
I had this headache a few months ago. I was trying to get a shell script running with admin privileges that shutdown my computer at a certain time. I feel your pain.
I used the BetterAuthorizationSample which was a total nightmare to wade through. But I took the most pragmatic route - I didn't bother trying to understand everything that was going on, I just grabbed the guts of the code.
It didn't take me that long to get it doing what I wanted. I can't remember exactly what I altered, but you're welcome to check out my code:
http://github.com/johngallagher/TurnItOff
I hope this helps on your quest for a secure application!

Resources