PDA

View Full Version : MSI Custom action dll requires Admin privilege?



janakp
07-11-2006, 04:19 PM
I have a MSI custom action C++ dll in Basic MSI project. It's stored in the binary table for the MSI. Everything works fine when I run the MSI on a machine where I have admin privileges.

But, if I run the MSI (w/ logging enabled using msiexec) on a machine where I don't have admin privileges, the installer directly goes to "setup interrupted..." message screen. When I look in the logs, the return value for custom action is 3.

So my guess is that MSI custom action dll's need elevated privileges to run on client machine? Any help will be much appreciated.

Here's the Test custom action code:

/// link to MSI Library
#pragma comment(lib, "msi")
//#pragma comment(lib, "psapi")

// include standard MSI & Win32 headers
#include < windows.h >
#include < string.h >
#include < tchar.h >
#include < msi.h >
#include < msiquery.h >
#include < stdlib.h >
#include < io.h >
#include < stdio.h >
//#include < sys/stat.h >
//#include "psapi.h"

// function prototypes
UINT LogMessage (MSIHANDLE, LPCWSTR);


// custom actions
UINT __stdcall TestCustomAction (MSIHANDLE hInstall)
{
LogMessage(hInstall, _T("went here"));

return ERROR_SUCCESS;
}

UINT LogMessage (MSIHANDLE hInstall, LPCWSTR szMsg)
{
PMSIHANDLE hRecord = MsiCreateRecord(1);
MsiRecordSetStringW(hRecord, 0, szMsg);
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO),hRecord);
return ERROR_SUCCESS;
}

And here is the snippet from the MSI log file:

MSI (c) (F8:B8) [13:36:13:091]: Doing action: TestCustomAction
Action 13:36:13: TestCustomAction.
Action start 13:36:13: TestCustomAction.
MSI (c) (F8:44) [13:36:13:106]: Invoking remote custom action. DLL: C:\DOCUME~1\JANAK~1.PAR\LOCALS~1\Temp\MSI1F.tmp, Entrypoint: TestCustomAction
MSI (c) (F8:C4) [13:36:13:122]: Cloaking enabled.
MSI (c) (F8:C4) [13:36:13:122]: Attempting to enable all disabled priveleges before calling Install on Server
MSI (c) (F8:C4) [13:36:13:122]: Connected to service for CA interface.
Action ended 13:36:13: TestCustomAction. Return value 3.
MSI (c) (F8:B8) [13:36:13:278]: Doing action: SetupCompleteError
Action 13:36:13: SetupCompleteError.
Action start 13:36:13: SetupCompleteError.

MichaelU
07-12-2006, 10:06 AM
While certain custom actions can themselves require administrator privileges, MSI in general does not require administrator privileges in order to run a custom action. Since you mentioned nothing about exports, the first thing I'd check in your case is if the TestCustomAction entry point is actually exported by your DLL. Dependency Walker or a quick LoadLibrary and GetProcAddress test should be able to confirm or deny this as the point of failure.

janakp
07-12-2006, 11:57 AM
I do have an exports with entry point to my TestCustomAction. Also ran DependencyWalker to verify. And it works on a machine where I have admin rights, so I figured that the dll definition is correct.

And my test function has very basic Msi function calls. That's why I was wondering if the Msi API requires admin privileges. To test that I removed the LogMessage call and replaced it with a simple variable declaration so that the custom action is not calling Msi API at all, still got the same error in the logs.

Thanks.

MartinMarkevics
07-12-2006, 01:18 PM
Can you change the code in TestCustomAction to just call a messagebox:

UINT __stdcall TestCustomAction (MSIHANDLE hInstall)
{
::MessageBox(0, _T("This is a test"), _T("This is a test"), MB_OK);
return ERROR_SUCCESS;
}

I don't specifically see anything wrong with your code, but this will rule it out and let us know whether it actually gets into your code or not.

janakp
07-12-2006, 01:31 PM
Hi Martin,

I replaced TestCustomAction with your code. Still same error in the msi log. And I didn't see the message box when I ran the MSI.

As a further test for my Msi API guess, I added an InstallScript custom action with the same exact calls to Msi API functions as my original Custom Action C++ dll functions (not TestCustomAction). That works!

Could it have anything to do with the fact that the MSI C++ dll is embedded into the MSI as a binary, and the MSI engine is extracting the dll and loading it into the TEMP folder (as the log indicates)? And this might require admin privileges on the machine?

Thanks.

janakp
07-12-2006, 01:33 PM
Also, I tested your TestCustomAction with MessageBox on a machine where I have admin privileges and it works, i.e., I see message box popup.

Christopher Painter
07-12-2006, 02:14 PM
As you see in the log, customactions are extracted to a TMP file for execution. Could there be a permissions problem on this folder that is blocking the module from being written and then loaded by LoadLibrary?

janakp
07-12-2006, 02:40 PM
that's exactly what I was thinking too (as I mentioned in my reply to Martin's post). Although, my windows domain login has full control access to the TEMP folder, and I verified that msiexec.exe is running as my windows domain login, when I ran the MSI.

About LoadLibrary not having execute permission in the TEMP folder, I don't really understand the internal workings that well to know under what security context LoadLibrary runs. I would assume it inherits the context of the calling process - msiexec, or maybe SYSTEM? SYSTEM does have full control on the TEMP folder.

Thanks.

Christopher Painter
07-12-2006, 02:50 PM
LoadLibrary is an Win32 API function that loads DLL modules into memory and returns a module handle. It runs in the context of the process that calls it so if it's an immeadiate CA it's the launching user and in defferred it's system impersonating the launching user. In deferred with Eleveated privs it's SYSTEM not doing any impersonation.

Other then looking at it first hand, the only other things I could suggest is some runtime profiling with Regmon and FileMon to compare the logs of a situation that works with one that doesn't work to see if any access error messages show up as the culprit.

And as said before, CA's generally work with or without Admin privs yet sometimes require Admin privs depending on what they are doing.

MartinMarkevics
07-12-2006, 03:47 PM
It shouldn't have anything to do with the temp directory, but can you manually create a file in that folder on your system? In a nutshell, InstallScript custom actions work exactly like your DLL custom action since it uses the temp directory.

What OS are you running this test on? Looking at your code, I presume that you are compiling this .dll as Unicode (as opposed to ANSI). That's fine if you are running on NT, but it won't work on 9X. Though I assume you're not running on 9X, but have to ask to make sure.

Also, another thing to try would be to validate your MSI package either using the IDE or ORCA. It's kind of a longshot, but sometimes that will point out subtle errors (like a bad foreign key to the Binary table) that causes something like this. Though, that's probably not the case here since it works in some instances.

janakp
07-12-2006, 04:17 PM
Thanks for your replies!

Chris, I'll try Regmon/Filemon and see if I find anything. It's an immediate custom action, so like you said it should be running under the context of the logged in user.

Martin, I was able to manually create a test file in the temp directory on the machine where the MSI fails. I am compiling the dll as unicode and the client machine is running Win XP SP 2. Let me try to change it to ANSI. I'll also try to validate the MSI with the IDE/ORCA.

Christopher Painter
07-12-2006, 04:20 PM
As an aside, PSAPI doesn't exist on 9x platforms. That's NT specific code.

janakp
07-12-2006, 07:14 PM
I ran the ORCA validator, no errors in binary table or custom actions.

Unfortunately can't run FileMon/RegMon on the machine where it fails because admin privs are required to run these utilities. I'll have to contact a sys admin to run it for me.

One of the pre-requisites for our MSI is Win XP, Win 2k3 or higher, so use of PSAPI shouldn't be an issue.

Thanks.

Christopher Painter
07-12-2006, 07:17 PM
I believe that FileMon and RegMon only need the `debug a process` right. ( I think it complains when you try to start it.)

So have an admin give the unprivlieged the user and confirm that your CA still won't run. Then test that RegMon/Filemon works. Cause naturally if you give that user Admin to run the tools you'll CA will probably start working.

janakp
07-13-2006, 10:32 AM
I had my login added to the Debugger Users group on the machine so that I could run FileMon. With debug a process rights the CA works.

I'm going to looking into the ACL SID string for LoadLibrary and see if I can find the minimum permission required for a dll to run.

Can't think of anything else to try other than using InstallScript which works. I didn't really want to use Installscript because it seems to bloat the MSI - by about 2.5 MB because it has to include the InstallScript engine dll + setup.rul. And also adds to the prep time for the install (btw, I did read about it on your blog Chris, great blog)

Thanks,
Janak

Christopher Painter
07-13-2006, 11:56 AM
Sure, domain specific languages have a trade off..... no more then someone writing .Net code needing a 25MB .Net Framework and 10MB of memory to start up an AppDomain and call an installer class object. :)

Personally my clients want bulletproof and cheap. My bill rate is way too high to be writing perfect C++ code so that's why I use InstallScript.

Maybe there is something about the way your building your C++ DLL that is requiring the debug permission. ( RobertDicakau could help here cause he knows alot more C++ then I do )

Checkout:

http://www.codeproject.com/tips/msicustomaction.asp

See if you can get this to build and work without debug permissions.

MartinMarkevics
07-14-2006, 12:25 PM
I attached a small VC6 dll project. Can you build it, then try it with your project just to rule out anything weird with your project. One would think that this is some sort of permission issue, but because InstallScript CAs run OK in the same environment, it makes me think there is some other issue going on here. Anyway, give this a try and let us know the results.

janakp
07-19-2006, 07:39 PM
Hi Martin,

You and Chris were right in that it was a VS IDE setting while compiling the DLL that was causing this issue. After some troubleshooting, I was able to identify the cause - my Dll was compiled using dynamic runtime library linking (/MD or /MDd switch). I had to switch it to static runtime linking (/MT or /MTd switch) and it worked.

I'm trying to understand why. As far as I understand, with static linking the C runtime library is embedded in the dll. Whereas with dynamic linking it refers to the runtime library on the target system? This explains the difference in the size of the dll's. With static linking, my dll grew by about 400k.

Although, what I don't understand is with dynamic linking why does the runtime library link not work on the target system if invoked by a non-admin/debugger user? When I go to msvcr80.dll on the filesystem on the target system (WinXP SP2), it has read/execute permissions for the Users group.

Thanks.

Christopher Painter
07-19-2006, 08:34 PM
While I could smell the problem, I don't have the skills to directly fix it. I would probably take the framework from one of the projects provided earlier and copy your fuctions into it. See if those will build and work with dynamic linking.

I wish I could help more, but my C experience is mostly limited to ANSI C on Unix and that was years ago. I've managed to write a few CA's in C++ but I try not to.

janakp
07-19-2006, 08:43 PM
Hi Chris,

The project that Martin included uses static linking. That's how I narrowed down the cause - I did a diff between my project that didn't work & his project that worked. And worked through each diff until I found the culprit.

I've posted the dynamic vs static linking on another C++ user group.

Thanks for all your help.
Janak

Christopher Painter
07-20-2006, 08:20 AM
I'd love to hear what you find out. I also have a buddy at work today who is real good with C++. I'll ask him what he can think of.