Extend IBM Security Access Manager for ESSO AccessProfile with Windows native libraries


In this article, learn about developing and embedding a Windows native library inside an IBM Security Access Manager for Enterprise Single Sign-On (ISAM ESSO) 8.2 AccessProfile. Walk through the steps of the component object model (COM) component registration based on the sample library. Business partners and consultants who develop and deploy ISAM ESSO product solutions might find this article especially helpful.

You can download the code examples used in this article.

ISAM ESSO AccessProfile

ISAM ESSO provides single sign-on (SSO) support for a wide range of applications, including Windows native GUI and console executables. The primary goal of ISAM ESSO is to respond to requests for user credentials (user name/password).

The response flow is defined within the AccessProfile, which resembles a flowchart. An ISAM ESSO AccessProfile is a set of instructions structured into an XML file that defines the automatic SSO or sign-off mechanism for an application. Each AccessProfile contains a set of definitions and states. Each state represents a specific situation where the state machine waits for certain events to occur and fires a trigger. The state machine model represents a sequence of steps such as logon/logoff, into/from the application, changing passwords, and so on.

Figure 1 shows an example of the ISAM ESSO AccessProfile. The first oval (Start state) activates a trigger to activate the main window. The main window activates a trigger to search the mainframe for the text, which then goes to the state to await input from the user.

Figure 1. ISAM ESSO AccessProfile
ISAM ESSO AccessProfile
ISAM ESSO AccessProfile

AccessProfile is interpreted by the ISAM ESSO Observer module. The Observer module is a Windows library and is injected using the Windows hooking mechanism into various sets of applications, which then consult the appropriate AccessProfile to perform the necessary logon/logoff actions. The Observer agent also listens to and captures operating system-level Windows messages, thereby functioning as an intermediary layer between applications and the OS.

For example, when an application creates a dialog window, the OS sends a message to this dialog procedure. The Observer agent intercepts the message and looks for a dialog title in the assigned AccessProfile for this application. If a match is found, the dedicated AccessProfile through the Observer agent controls the rest of the application flow according to the allotted state machine.

AccessProfiles are created and maintained within the AccessStudio, which is one of the tools in the ISAM ESSO component suite. AccessStudio requires that the AccessAgent be installed on the same machine. Although AccessStudio lets you control many of the OS messages and properties, sometimes there is a need for more advanced features. Some customers might need to leverage existing ISAM ESSO capabilities beyond what's available using standard profiling components and methods. Such a requirement could arise when writing console-based application profiles for Windows command-line and IBM Personal Communications.

By design, ISAM ESSO doesn't expose any profiling mechanism to monitor the Windows clipboard buffer interfaces and messages. When the user injects some text into the console window, there is no standard way to intercept this action by AccessProfile. Why is this important? Many users are used to copying/pasting credentials from another source, such as a text document. In such a case, by design, ISAM ESSO would not be able to properly capture those credentials when supporting console-based applications.


To follow along with the examples in this article, you'll need to prepare the following environment. (Basic installation and configuration steps are not within the scope of this article.) On a Windows system, install and configure:

  • Tivoli Access Manager Enterprise for Single Sign-on (TAM E-SSO) AccessAgent in version 8.1 or higher
  • Tivoli Access Manager Enterprise for Single Sign-on (TAM E-SSO) AccessStudio in version 8.1 or higher
  • Microsoft Visual Studio 2010 (optional, used to rewrite the PCOMMHelper library)

The example implementation was tested on Windows XP and Windows 7 with User Account Control disabled. If User Account Control is enabled, some problems might occur during the initial COM component registration procedure because this process requires writing predefined entries into the Windows Registry. The COM component registration is done in the same manner as that of the Regsvr32 Windows command and the same administrative permissions are required.

Sample code

You can download the sample files used in this article to do a real test of capturing pasted text buffer (like user credentials) using IBM Personal Communications. The testPcomm.eas file contains AccessProfile, ready to be uploaded into the IMS server. It can also be used in AccessStudio test mode. The example AccessProfile already has the PCOMMHelper library embedded into VBScript sample code so no initial alteration is necessary.

Beyond standard ISAM ESSO profiling techniques

As mentioned, sometimes standard profiling mechanisms require additional tricks. ISAM ESSO lacks a direct way to monitor and capture user credentials from the Windows clipboard buffer. Though users typically enter their credentials using keystrokes, you want to support both input credential techniques. Using the Windows native library would be one trick to overcome the issues encountered when writing ISAM ESSO AccessProfiles. (Later in the article, the AccessProfile solution for the IBM Personal Communications (PCOMM) application is covered.)

The first problem to be solved is: How do you extend an existing AccessProfile, using the Windows library, without using separate delivery methods? Keep in mind, one of the main advantages of ISAM ESSO is the smooth and seamless synchronization of the profiles between a group of users and the IMS server (a centralized database of user identities, AccessProfiles, and authentication policies) without any prior user intervention. If a group of users tends to be huge, separate delivery of such a library file could prove to be difficult and time-consuming. As another route, consider embedding the library file directly into the profile. The obvious container for this seems to be VBScript code.

One way to augment standard profiling techniques is to use VBScript or JavaScript code that performs custom action needed as part of a workflow action inside an AccessProfile. Such a script can make a direct call into the Windows OS to execute prescribed tasks.

For the example case, you want to use VBScript code extension:

  • As a static container for the library file
  • To decode and unpack the library file into the Windows file system
  • To register the unpacked library as a COM interface
  • To load the library file into the intended process object space

Figure 2 shows an example of the VBScript, prepared to be executed right after PCOMM Window activation.

Figure 2. VBScript
Screen showing VBScript in the Language field of the Script Editor window.
Screen showing VBScript in the Language field of the Script Editor window.

The library file part is assigned to the Native variable. Of course, you can't directly include a binary file into the VBScript code, which is why some encoding techniques need to be used. In this case, a binary file was encoded into a string of bytes using hexadecimal notation. The solution lets you freely store the output in any ASCII text file. Listing 1 uses the file as temporary storage, which must be copied into your AccessProfile VBScript code. The external VBScript code in Listing 1 also uses the MakeCab command to initially compress the library file into a cabinet (.cab) file.

Listing 1. External VBScript code
Const ForReading = 1, ForWriting = 2, adTypeBinary = 1
Dim bin, bBytes

Set bin = CreateObject("ADODB.Stream")
Set fso = CreateObject("Scripting.FileSystemObject")
Set objShell = CreateObject("WScript.Shell")

If  (fso.FileExists ("PCOMMHelper.dll")) Then 
	Set objExec = objShell.Exec ("MakeCab PCOMMHelper.dll")
end if

Do While objExec.Status = 0
	WScript.Sleep 250

Set OutFile = fso.OpenTextFile("", ForWriting, true)
bin.Type = adTypeBinary
bin.LoadFromFile ""
size = bin.Size
Binary = bin.Read()

ReDim byteArray(LenB(Binary))
For i = 1 To LenB(Binary)
	znak = AscB(MidB(Binary, i, 1))
	if (znak < 16) Then 
		OutFile.Write "0"
	End If
	OutFile.Write hex(znak)
For x = 1 To size 
	OutFile.Write byteArray(x)

Windows data compression techniques are used to save some space before the real encoding process starts— you want to use the AccessProfile storage as efficiently as possible.

The reverse process, as in Listing 2, simply reads this hexadecimal string (the native variable) to an array and writes back into the file system as a compressed library file.

Listing 2. Reverse process
Sub WriteBinary(FileName)
On Error Resume Next
        Dim Buf()
        Dim I, aBuf, Size, bStream
        ReDim Buf(Len (native)/2)

        For I = 0 To Len(native)-1 Step 2
                Buf(I\2) = Clng("&h"&Mid(native, I+1, 2))

        Size = UBound(Buf): ReDim aBuf(Size)
        For I = 0 To Size-1 Step 2
                aBuf(I \ 2) = ChrW(Buf(I + 1) * 256 + Buf(I))
        If I = Size Then aBuf(I \ 2) = ChrW(Buf(I))
        aBuf = Join(aBuf, "")
        Set bStream = CreateObject("ADODB.Stream")
        bStream.Type = 1
        With CreateObject("ADODB.Stream")
                .Type = 2 : .Open: .WriteText aBuf
                .Position = 2: .CopyTo bStream: .Close
        End With
        bStream.SaveToFile FileName, 2
        Set bStream = Nothing
End Sub

In both scripts, the ADODB.Stream object is used for seamless conversion between the stream of binary data and text.

Now it's time for cabinet (.cab) file decompression using the Expand command, as in Listing 3.

Listing 3. Cabinet file decompression
Function ExpandDll()
tempPath = objShell.ExpandEnvironmentStrings("%USERPROFILE%") & "\"
statusFail = 0
fCabName = ""
fDLLName = "PCOMMHelper.dll"
 Do While (True)
	WriteBinary (tempPath&fCabName)
	If  (fso.FileExists (tempPath&fCabName)) Then 
		Set objExec = objShell.Exec ("EXPAND " & """" & tempPath&fCabName 
           & """" & " " & """" & tempPath&fDLLName & """")
		Exit Do
	end if

	Do While objExec.Status = 0

	If  (fso.FileExists (tempPath&fDLLName) =0 ) Then
		pc.SetPropValue "PCOMMHelper Error: ", "EXPAND failed"
	end if
End Function

The destination directory for the library file is the user's Home directory defined by the %USERPROFILE% environment variable. Using that directory assures there won't be any Access Denied problems.

At this point, you can conclude that the Windows library file is in place and ready to be registered as a COM interface. You might be wondering why you need to employ such complicated native code invocation methods. Isn't there a simpler way to do this stuff? The problem is: An AccessProfile doesn't have any possible way to directly call a Windows library. The only way to do so is through VBScript code and, by design, most of those scripting languages don't let you load native libraries into process object space. The initial objective now is to embed and execute some code within the application itself.

The COM component

In this section, you register the Windows library in the OS (to imitate the behavior of the real COM component) so it can be called directly from AccessProfile VBScript code using the CreateObject function.

Basically, when CreateObject is called, Windows looks up the registry to find out how the object should be instantiated. It then tries to load the corresponding library and creates the object from it. This registry look-up process is done twice. At first, given some identifier, the check is done under the HKEY CLASSES ROOT entry (in the example, this identifier is PCOMMHelper). If such an identifier is found, the corresponding CLSID value is picked up and used for the second check—this time under the HKEY CLASSES ROOT\CLSID key.

If the second check is also matched, the InprocServer32 subkey default value is used to grasp the full path where the Windows library has been installed. This process has been greatly simplified by taking into account the real production code COM component registration procedure, which should fulfill the basic requirements for the example. The next step is to bind the PCOMMHelper identifier to the library file.

First, check whether the COM component is already registered, as in Listing 4.

Listing 4. COM component
Function testDLL()
On Error Resume Next
	gPimsCurVer = ReadRegistry("HKCR\" & fDLLObject & "\CurVer\")
	gPimsCLSID  = ReadRegistry("HKCR\" & fDLLObject & "\CLSID\")
	dllLocation = ReadRegistry("HKCR\CLSID\" & gPimsCLSID & "\InprocServer32\")

	If Left (gPimsCurVer, Len ("Invalid")) = "Invalid"  Or Left (gPimsCurVer, 
       Len ("Unable")) = "Unable" Or Left (gPimsCLSID, Len ("Invalid")) = "Invalid" Or 
    Left (gPimsCLSID, Len ("Unable")) = "Unable" Or Left (dllLocation, 
       Len ("Invalid")) = "Invalid" Or Left (dllLocation, Len ("Unable")) = "Unable" Then
	End If

	Set PCOMMHelperAgain = CreateObject("PCOMMHelper")
       If Err = -2147221231 Or Err = 0 Then
            pc.SetPropValue "PCOMMHelper ", "Enabled"
            pc.SetPropValue "PCOMMHelper ", "Disabled"
	End If
End Function

Listing 4 mimics the steps of the COM component look-up procedure. If any of them fail registration, the process needs to be done. If registration criteria are fulfilled, you use the CreateObject function again to check if it's possible to load the PCOMMHelper.dll library. (The exact error value -2147221231 that was checked in Listing 4 is related to incomplete initialization of the COM component. The goal in the example is to just load a library into process object space and not to play with COM interfaces.)

Listing 5 shows the COM component registration procedure.

Listing 5. COM component registration procedure
Function WriteCOMToRegistry ()
   tempPath 	= objShell.ExpandEnvironmentStrings("%USERPROFILE%") & "\"
   fDLLName 	= "PCOMMHelper.dll"
   fDLLObject 	= "PCOMMHelper"

   For i=1 To Len (TypeLib.Guid)
	Char = mid (TypeLib.Guid , i, 1)
	if Char <> "}" Then
		CLSID = CLSID & Char
		CLSID = CLSID & "}"
		Exit For
	End If
   ret = WriteRegistry ("HKEY_CLASSES_ROOT\" & fDLLObject & "\" , "PCOMMHelper Class", 
   ret = WriteRegistry ("HKEY_CLASSES_ROOT\" & fDLLObject & "\CurVer\", fDLLObject & 
      ".1", "REG_SZ")
   ret = WriteRegistry ("HKEY_CLASSES_ROOT\" & fDLLObject & "\CLSID\", CLSID, "REG_SZ")
   ret = WriteRegistry ("HKEY_CLASSES_ROOT\CLSID\" & CLSID & "\" , "PCOMMHelper Class", 
   ret = WriteRegistry ("HKEY_CLASSES_ROOT\CLSID\" & CLSID & "\InprocServer32\" , 
      tempPath & fDLLName, "REG_SZ")
   ret = WriteRegistry ("HKEY_CLASSES_ROOT\CLSID\" & CLSID & 
      "\InprocServer32\ThreadingModel" , "Apartment", "REG_SZ")
   ret = WriteRegistry ("HKEY_CLASSES_ROOT\CLSID\" & CLSID & "\ProgID\" , fDLLObject & 
      ".1" , "REG_SZ")
   ret = WriteRegistry ("HKEY_CLASSES_ROOT\CLSID\" & CLSID & "\Programmable\" , "" , 
   ret = WriteRegistry ("HKEY_CLASSES_ROOT\CLSID\" & CLSID & "\VersionIndependentProgID\" 
      , fDLLObject , "REG_SZ")
End Function

The first half of the WriteCOMToRegistry function generates the new unique CLSID value (globally unique class identifier expressed in hexadecimal notation, enclosed within a pair of curly braces). CLSID is a 16-byte (128-bit) array that's filled in with a unique series of bytes. Theoretically, it can never have the same series of bytes when generated again. In the COM world, CLSID values, also known as GUID, are used to uniquely distinguish different software component interfaces.

At first glance, this procedure might not make a lot of sense. You might wonder why you don't generate a CLSID once and pre-assign this in the script. Isn't that the typical procedure when writing COM components? The simple answer is: It's in the script for testing and troubleshooting scenarios. You're free to replace this part according to your objectives.

Also of note here is that, typically, every COM object must have a QueryInterface function in which a preassigned CLSID value is checked. In the example, a Windows library fulfills the very basic criteria just enough to load it into the process object space—which definitely doesn't fulfill the full-fledged COM component requirements.

As mentioned, one goal of the proof-of-concept is to use AccessProfile storage efficiently. Keeping the initial size of the library as small as possible is crucial here (playing with COM interface development deviates from the purpose). The example library contains only two functions as far as COM component implementation is concerned:

  • DllGetClassObject
  • DllCanUnloadNow

The second half of the WriteCOMToRegistry function is used to mimic the Regsvr32 Windows command (used to register or un-register a COM component) functions and to cover the steps of the look-up procedure outlined above. The Regsvr32 tool doesn't do much; it loads the library file passed in the command-line argument and subsequently calls some COM registration functions in this library. From that point, it's up to the COM component implementation to register itself. In the simplest form, this registration process just adds appropriate registry keys under the HKEY CLASSES ROOT entry.

If you're interested in further investigation, it is recommended you use the Process Monitor tool (see Related topics). It lets you see exactly which Windows Registry keys are used by the Regsvr32 Windows command during the COM registration process.

AccessProfile scenario for IBM Personal Communications

The primary purpose of the PCOMMHelper library is to capture user credentials from the Windows Clipboard buffer and make them available to standard ISAM ESSO AccessProfile triggers. The following scenario simulates AccessProfile for the IBM Personal Communications (PCOMM) application. (The exact same use case is true for the Windows command line).

  • User runs the application and waits for command input to appear on the screen.
  • On the Edit drop-down menu, user selects Paste, as in Figure 3, and confirms with the CTRL/Enter key.
Figure 3. Using Windows Clipboard buffer with IBM Personal Communications
A PCOMM window with the Edit drop-down menu shown and Paste option highlighted.
A PCOMM window with the Edit drop-down menu shown and Paste option highlighted.

Figure 4 shows the AccessProfile scenario for the use case.

Figure 4. AccessProfile
Screen with Account data bag id, add field of type, and account data item template id filled in.
Screen with Account data bag id, add field of type, and account data item template id filled in.

Upon selecting CTRL/Enter key, the profile should be able to capture what has been pasted to the command input. Unfortunately, the standard trigger for monitoring keyboard input will not be able to capture any text pasted into the screen unless this text is somehow injected again by simulating keystrokes. This is exactly what the PCOMMHelper library does.

Implementing PCOMMHelper

In this section, let's try to depict the flow of sequences upon CreateObject ("PCOMMHelper") execution. Remember, the PCOMMHelper library contains very a basic implementation of a COM component, so there would be no calls from the OS into standard COM initialization methods. After the registry look-up process picked up the PCOMMHelper.dll library from User's Home directory and loaded it into the PCOMM process object space, the Entry point function DllMain is called. This is the standard WINAPI procedure to deal with Windows native libraries (the system calls the DllMain function with the DLL_PROCESS_ATTACH value as the reason for the call).

Listing 6 shows a DllMain snippet from the PCOMMHelper library.

Listing 6. DllMain snippet
BOOL WINAPI DllMain(HINSTANCE instance, DWORD fdwReason, LPVOID lpvReserved)
    switch (fdwReason)
            EnumWindows ((WNDENUMPROC)EnumWindowsProc, GetCurrentProcessId());
            if (PCOMMhwnd)
                lpfnOldLoginWndProc = (WNDPROC) SetWindowLong ((HWND)PCOMMhwnd, 
                GWL_WNDPROC, (LONG)LoginWndProc);
            if (lpfnOldLoginWndProc)
                SetWindowLong ((HWND)PCOMMhwnd, GWL_WNDPROC, (LONG)lpfnOldLoginWndProc);

The first step is to get the PCOMM process window handle. You need to enumerate all visible windows on the screen, looking for the identifier of the process that created that window. This process identifier will be compared with the identifier of the process that you've called your PCOMMHelper library from: pcsws.exe (IBM Personal Communications - PCOMM). If this is somewhat hazy, remember that you called CreateObject ("PCOMMHelper") from the VBScript code executed by the profile that's interpreted by the ISAM ESSO Observer module initially injected to the process object space using the ISAM ESSO engine.

Figure 5 shows all the controls belonging to the PCOMM application (captured using Winspector).

Figure 5. Controls
A PCOMM window, with the controls listed to the left.
A PCOMM window, with the controls listed to the left.

Most of the time there will be more than one window (control) belonging to the PCOMM process, so you need to find the parent window attributed by the PCSWS:Main:00400000 class.

Listing 7 shows a snippet of EnumWindowsProc from the PCOMMHelper library.

Listing 7. EnumWindowsProc
BOOL CALLBACK EnumWindowsProc (HWND hwnd, LPARAM ProcID)
    DWORD      dwID=0 ;
    HWND        testHwnd=NULL;
    char             wBuffer[256]={0};

    GetWindowThreadProcessId(hwnd, &dwID) ;
    if(dwID == ProcID)
        if (GetWindowLong(hwnd, GWL_HWNDPARENT) == 0)
            if(GetClassName (hwnd, wBuffer, sizeof (wBuffer))>0)
                if (_stricmp(wBuffer, "PCSWS:Main:00400000")==0)
                    PCOMMhwnd = hwnd;
    return TRUE ;

Now that you've obtained the window handle, it's time for the major task: Subclassing an instance of the PCOMM process main window using the SetWindowLong function. You'll hijack the control of that particular application window, enabling you to monitor and evaluate all the internal messages passed between that process and OS. At first glance, this might seem hazardous, but it's actually exactly what the ISAM ESSO Observer module does—listen and capture OS-level Windows messages. You'll just be "extending" the Observer module functions for the purposes of the example.

Though you could react to every message being sent to that window, you only care about the WM_COMMAND message, which is sent when the user selects an item from a menu. For the PCOMM application, the ID of this menu item (Paste) has the value 0x414 in hexadecimal notation. In this use case scenario, assume that the user does select the Paste command from the drop-down menu. To make sure things run as smoothly as possible, create a separate thread (SendWindowText) for this key part and give control back to the monitoring procedure.

The last part of the monitoring procedure takes care of passing messages back into the original PCOMM process (using the CallWindowProc function), thereby making sure your interference will be almost unnoticed.

Listing 8 shows a LoginWndProc snippet from the PCOMMHelper library.

Listing 8. LoginWndProc
LRESULT CALLBACK LoginWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    WORD        wID = 0;
    HANDLE    hThread = NULL; 
    DWORD     dwThread; 

    switch (message)
    case WM_COMMAND:
        wID = LOWORD(wParam);	
        if (0x414 == wID)
            hThread = CreateThread(NULL, NULL,(LPTHREAD_START_ROUTINE) SendWindowText,
hwnd, NULL, &dwThread);
            CloseHandle (hThread);

    if (lpfnOldLoginWndProc)
        return CallWindowProc (lpfnOldLoginWndProc, hwnd, message, wParam, lParam) ;

It's time for the last crucial step: To simulate keystrokes so the standard trigger for monitoring keyboard input is able to capture what has just been pasted by the user. Basically, the whole clipboard text buffer is sent to the PCOMM process window, one character at a time, using the SendMessage function call. To summarize, the example intercepted the WM_COMMAND message sent upon user drop-down menu selection and, after slight modification, sent back another message that essentially simulates the user's keystrokes.

Listing 9 shows a snippet of SendWindowText from the PCOMMHelper library.

Listing 9. SendWindowText
void SendWindowText (HWND hwnd)
    char *buffer                  = NULL;
    HANDLE hData            = NULL;
    BOOL ClipboardOpened = FALSE;

    ClipboardOpened = OpenClipboard(hwnd);
    if (ClipboardOpened)
        hData = GetClipboardData(CF_TEXT);
        if (hData)
            buffer = (char*)GlobalLock( hData );

    if (buffer && strlen(buffer)<128)
        int i = 0;
        for (i=0; i<strlen(buffer); i++)
            if (isprint_ (buffer[i]))
                 SendMessage (hwnd, WM_CHAR, buffer[i], 1L);
        MessageBox (NULL, "No support for Clipboard buffer bigger than 128 characters", 
           "PCOMMHelper", NULL);

    if (hData)
    if (ClipboardOpened)

Building the PCOMMHelper.dll

This section covers a few techniques to minimize the size of the PCOMMHelper.dll library so that AccessProfile storage is used in the most efficient manner. Squeezing the COM component implementation was effective, but there is still a lot of space to eliminate from the average library file.

The example achieved a size of 4KB with about 1KB of empty space, which is easily compressible by the MakeCab Windows command. Theoretically, the 1KB of space could be cut off by merging some sections (such as code and data) of the file together, but such a process is risky and the library could potentially be rejected by the OS. (Typically, most executables and library files have more than one section, each of which is aligned on a 512byte boundary.)

Table 1 shows the settings that were used during compilation and the linking phase.

Disable Run-Time Type Information This will disable checking and storing information about an object's data type.
Don't Generate ManifestYou don't want to allow Visual Studio to embed the XML manifest resource into the library file. Manifest resource is a type of XML file describing the executable file properties. Though it can be useful when dealing with User Account Control on Windows 7, you can't waste valuable space for this.
Don't Use Unicode Character SetJust use the ANSI versions of all Windows functions.
Don't Generate Debug InfoSelf-explanatory.


ISAM ESSO provides native techniques to support a wide range of applications. For most applications, standard profiling components and methods are more than sufficient. However, when you need to extend the reach beyond current limits, using a Windows native library might help do the trick. The example solution in this article was tested on an internal IBM project.

Downloadable resources

Related topics


Sign in or register to add and subscribe to comments.

Zone=Security, Tivoli
ArticleTitle=Extend IBM Security Access Manager for ESSO AccessProfile with Windows native libraries