1
votes

We have a configuration application which contains the server and client setup details of our software which is managed by network administrators. In this app we can save admin user credentials (which are encrypted) to use for our main software installations.

The problem encountered during our auto upgrade code is that the administrator user credentials passed to run the msiexec command is that the account does not have administrative permissions even though the user is marked as local administrator on the target pc. The same problem was encountered using the domain administrators' user credentials. Manual un-installations using the msi GUI go through just fine when logged on as that particular admin user. The only time the code goes through successfully uninstalling the software, is if I get the domain admin to temporarily save the main administrator user credentials in the config application (for testing purposes of course), which is not the intention of being able to save admin credentials in the config application.

The following are the details extracted from the msiexec log detailing the error encountered:

MSI (s) (80:3C) [11:01:00:674]: Machine policy value 'AlwaysInstallElevated' is 0
MSI (s) (80:3C) [11:01:00:674]: User policy value 'AlwaysInstallElevated' is 0
MSI (s) (80:3C) [11:01:00:674]: MSI_LUA: Elevation prompt disabled for silent installs
MSI (s) (80:3C) [11:01:00:674]: Note: 1: 1730 Action start 11:01:00: InstallInitialize.
MSI (s) (80:3C) [11:01:00:674]: Product: Client Software -- Error 1730. You must be an Administrator to remove this application. To remove this application, you can log on as an Administrator, or contact your technical support group for assistance.

Now for the code part: This is the uninstall setup prior to calling the ExecuteInstaller method

                //uninstall...
                if (log.IsInfoEnabled)
                {
                    log.Info("Autoupdate: uninstalling current client.");
                }
                string uninstallCommand = String.Format("/quiet /uninstall {0}", productCode);

                if (log.IsDebugEnabled)
                {
                    log.Debug("Autoupdate: Adding verbose msi logging to \"msiClientUnInstallLog.log\"");
                    uninstallCommand += " /l*v \"msiClientUnInstallLog.log\"";
                }
                ExecuteInstaller(uninstallCommand);

Now to run the process:

    private void ExecuteInstaller(string command)
    {
        if (log.IsDebugEnabled)
        {
            log.Debug("Autoupdate: executing command: " + command);
        }
        ProcessStartInfo startInfo = new ProcessStartInfo("msiexec.exe", command);
        startInfo.UseShellExecute = false;
        startInfo.CreateNoWindow = true;
        startInfo.WindowStyle = ProcessWindowStyle.Hidden;

        if (log.IsDebugEnabled)
        {
            log.Debug("Autoupdate: reinstall using elevated priviledges.");
        }
        startInfo.Verb = "runas";

        if (!String.IsNullOrEmpty(BaseSettings.Environment.Server.ClientInstallerUserName))
        {
            if (log.IsDebugEnabled)
            {
                log.DebugFormat("Autoupdate: an installer username was specified: {0}, domain: {1}. Installing using supplied credentials...", BaseSettings.Environment.Server.ClientInstallerUserName, BaseSettings.Environment.Server.ClientInstallerDomain);
            }
            startInfo.Domain = BaseSettings.Environment.Server.ClientInstallerDomain;
            startInfo.UserName = BaseSettings.Environment.Server.ClientInstallerUserName;
            /*Hidden: Decrypt the encrypted admin password and save secure string to new parameter.*/
            System.Security.SecureString installPassword = new System.Security.SecureString();
            /*Hidden: Decrypt the encrypted admin password and save secure string to new parameter.*/
            startInfo.Password = installPassword;
            startInfo.UseShellExecute = false;
        }

        Process process = Process.Start(startInfo);
        if (process != null)
        {
            process.WaitForExit();
            if (log.IsDebugEnabled)
            {
                log.DebugFormat("Autoupdate: command finished with exit code: {0}", process.ExitCode);
            }
            if (process.ExitCode != 0)
            {
                string errorMessage = String.Format("Autoupdate: The upgrade of this Client failed. Please ask an administrator to upgrade it for you.");
                log.Error(errorMessage);
                throw new Exception(errorMessage);
            }
        }
    }

The steps undertaken are reproducible from within cmd prompt even with elevated privileges. For example:

C:>runas /user:domain\user "msiexec.exe /quiet /uninstall {936DDA62-6793-4713-999 7-E249CD61D3CB} /l*v \"C:\msiClientInstallLog.log\""

Running with a domain user which is an administrator on the target pc, results in nothing happening. Only if you use the main domain\administrator credentials does this go through successfully

Other settings we attempted to change which result in successful un-installations is when we change the registry setting: HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA to 0. However the point is not to disable the UAC but rather to use the correct channels to bypass the UAC. Specifying the main network admin password though does not fall part of that strategy. An administrator account with such privileges should suffice.

There must be a GPO setting which will allow silent installations for the specified admin account credentials.

Is it possible to call the UAC elevation prompt somehow?

Alternatively would digitally signing our msi's work?

Does anyone have any hints, links or other valuable advice for me to try? I haven't found anything valuable on the net yet...

2

2 Answers

0
votes

While this might not be the perfect solution to my problem (and I therefore won't mark it as the answer): One solution for me thus far from trial and error was to execute from cmd the following uninstall command:

C:>runas /user:domain\user "msiexec.exe /qr /uninstall {936DDA62-6793-4713-999 7-E249CD61D3CB} /l*v \"C:\msiClientInstallLog.log\""
(enter the user password and press enter when prompted)
(Note the change from "quiet" to "qr" from my original question)

In addition to that I had to ensure that the UAC setting (found in Control Panel > User Accounts > Change User Account Control Settings) was set to at least the second lowest option of the available four; i.e. Notify me only when apps try to make changes to my computer (do not dim my desktop). Previously my UAC was set to Never notify because who wants to see these pesky popups anyway.

Having this option set will prompt you with a UAC prompt to allow or deny permission to proceed. If you opt to proceed, the installer is finally elevated and the verbose log indicates the following detail:

MSI (c) (D4:58) [13:05:35:265]: MSI_LUA: Setting AdminUser property to 1 because this is the client or the user has already permitted elevation
MSI (c) (D4:58) [13:05:35:265]: PROPERTY CHANGE: Adding AdminUser property. Its value is '1'.
MSI (c) (D4:58) [13:05:35:265]: MSI_LUA: Setting MsiRunningElevated property to 1 because the install is already running elevated.

and subsequently successfully uninstalls the software.

I'm still looking for/researching a better, more automated option which I can execute from within my C# code.
Please continue to help me find a better solution to this workaround.

0
votes

Not sure why I didn't try this solution before:

Use msiexec.exe option /passive together with the /quiet parameter. This displays the uninstall/install progress bar during uninstall and the install process and also prompts you UAC if you have it enabled.

So in essence the solution for me is as follows:
1. Same as my previous answer, I needed to raise the UAC level from None to Notify me only when apps try to make changes to my computer (do not dim my desktop).
2. change the following c# code to contain the /passive parameter

string uninstallCommand = String.Format("/quiet /passive /uninstall {0}", productCode);

You still have to remember to Run your application as administrator to get the logging to work. What this did however is prompt UAC permission which elevates the installer permission to admin level and proceed to uninstall the software successfully.