Friday, April 27, 2012

Microsoft Installer Custom Actions User Impersonation

Problem

When creating an installer with custom actions you might run into some security issues when executing it on a Windows Vista/7/2008 or later OS.
This is because custom actions will be executed in the context of the user running the Windows Installer Service being the SYSTEM user.
This behavior is enforced from Windows Vista on and can give you authorization problems for certain tasks you want to perform in your custom actions.

Solution

To overrule this default behavior and run the custom actions as the impersonated user that is executing the MSI we will have to flip the msidbCustomActionTypeNoImpersonate bit that is on by default.
In a Visual Studio Setup project it is however not possible to set this flag through the Properties Window.
To solve this we must create a post-build script that will flip this bit:
// CustomAction_Impersonate.js <msi-file>
// Performs a post-build fixup of an msi to change all deferred custom actions to Impersonate
// Constant values from Windows Installer
var msiOpenDatabaseModeTransact = 1;

var msiViewModifyInsert         = 1
var msiViewModifyUpdate         = 2
var msiViewModifyAssign         = 3
var msiViewModifyReplace        = 4
var msiViewModifyDelete         = 6

var msidbCustomActionTypeInScript       = 0x00000400;
var msidbCustomActionTypeNoImpersonate  = 0x00000800

if (WScript.Arguments.Length != 1)
{
       WScript.StdErr.WriteLine(WScript.ScriptName + " file");
       WScript.Quit(1);
}

var filespec = WScript.Arguments(0);
var installer = WScript.CreateObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);

var sql
var view
var record

try
{
       sql = "SELECT `Action`, `Type`, `Source`, `Target` FROM `CustomAction`";
       view = database.OpenView(sql);
       view.Execute();
       record = view.Fetch();
    //Loop through all the Custom Actions
       while (record)
       {
           if (record.IntegerData(2) & msidbCustomActionTypeInScript)
           {
               //We must flip the msidbCustomActionTypeNoImpersonate bit only for deferred custom actions
               record.IntegerData(2) = record.IntegerData(2) & ~msidbCustomActionTypeNoImpersonate;
              view.Modify(msiViewModifyReplace, record);
           }
        record = view.Fetch();
       }

       view.Close();
       database.Commit();
}
catch(e)
{
       WScript.StdErr.WriteLine(e);
       WScript.Quit(1);
}

This script file (CustomAction_Impersonate.js) must be placed in the same folder as your setup project (Setup.vdproj) and add the following PostBuildEvent in your setup project:
cscript.exe "$(ProjectDir)CustomAction_Impersonate.js" "$(BuiltOuputPath)"

Build and run your setup project and you will notice that all the custom actions will now run as the impersonated user that executes the MSI.

Author: Christophe

Monday, April 23, 2012

General Access Denied error (0x80070005) in Hyper-V Manager when starting VM

I ran into the following error when I tried to start a VM in Hyper-V Manager recently:



To solve this issue I had to grant Full control to the folder containing all the VM's files (Virtual Hard Disks, config,...) using the following command:

icacls <VM directory> /grant "NT VIRTUAL MACHINE\0BA74464-10AF-4709-AAE9-4C1B196C08ED":(OI)(CI)F /t

After executing this command you can verify if it was successful by checking the Security properties of the directory where the VM files are located through Windows Explorer. The user that corresponds with the Virtual machine ID in the error message should have Full control of the folder where the VM files are located.




Author: Kristof Lievens

Tuesday, April 17, 2012

Graceful shutdown of a webMethods Integration Server through custom developed java program

Out of the box a webMethods Integration Server is shutdown via the Administrator screen.

Following code snippet with allow you to gracefully shutdown a webMethods Integration Server through the usage of a custom developed java program.

Java code snippet :
import java.io.*;
import com.wm.app.b2b.client.*;
import com.wm.util.*;
import com.wm.data.*;

public class IntegrationServerShutdown
{
            public String hostName = null;
            public String port = null;
            public String userName = null;
            public String password = null;
            Context context = null;
            public boolean connected = false;
            public static void main(String[] args)
            {           String input = null;
                        //Create an instance of a b2bServer
                        IntegrationServerShutdown b2bServer = new IntegrationServerShutdown();

                        b2bServer.hostName=args[0];
                        b2bServer.port=args[1];
                        b2bServer.userName=args[2];
                        b2bServer.password=args[3];
                        System.out.println("");
                        b2bServer.connect();
                        if(!b2bServer.connected)
                                    System.exit(0);
                        b2bServer.shutdown();

            }
            public void connect()
            {           context = new Context();
                        try
                        {           context.connect(hostName + ":" + port, userName, password);
                                    System.out.println("Connected to " + hostName + ":" + port);
                                    connected = true;
                        }
                        catch(ServiceException e)
                        {           System.out.println("Could not connect to " + hostName + ":" + port);
                                    System.out.println(e.toString());
                                    connected = false;
                        }
            }
            public void disconnect()
            {           if(context != null)
                                    context.disconnect();
            }
            public void shutdown()
            {           try
                        {
                                    System.out.println("Shutting down IS Server... ");
                                    IData inputs = IDataFactory.create();
                                    IDataCursor myCursor = inputs.getCursor();
                                    myCursor.insertAfter("bounce", "no");
                                    myCursor.insertAfter("timeout", "0");
                                    myCursor.insertAfter("option", "force");
                                    context.invoke("wm.server.admin","shutdown",inputs);
                                    System.out.println("IS Server shutdown complete.");
                                    System.exit(0);
                        }
                        catch(ServiceException e)
                        {           System.out.println("IS Server shutdown failed.");
                                    System.out.println(e.toString());
                                    System.exit(1);
                        }
            }

}

Remarks :

  1. When compiling and running the java program make sure to include files wm-isclient.jar and mail.jar in your class path. File wm-isclient.jar can found in the “common/lib” folder of an Integration Server whereas file mail.jar is localized in the “ext” sub folder of the “common/lib” folder.
  2. Usage of the IntegrationServerShutdown program is IntegrationServerShutdown <IS hostname> <IS port> <IS account with administrative privileges> <IS account password>
  3. At this moment the java program is working with at least webMethods Integration Server v7.1.2 and v8.2. Specifications contained herein are potentially subject to change so use them at your own risk.
Author: Johan De Wulf