Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Enhancing WebSphere MQ JMS security using the Object Authority Manager

Ben Ritchie (ben.ritchie@uk.ibm.com), Staff Software Engineer, WebSphere MQ Client Development, IBM
Ben Ritchie is a software engineer at the IBM Hursley Software Lab in the UK, and he works as a Java developer on the WebSphere MQ Client Development team. Ben joined IBM in 2001 after completing a DPhil in astrophysics at the University of Sussex, and has worked with JMS ever since, first with WebSphere MQ Everyplace and more recently with WebSphere MQ. You can contact Ben at ben.ritchie@uk.ibm.com.

Summary:  This article shows you how to implement a custom authorization service using the WebSphere MQ Object Authority Manager to authenticate connection requests from WebSphere MQ JMS clients.

Date:  14 Dec 2005
Level:  Intermediate

Activity:  4230 views
Comments:  

Introduction

Java™ Message Service (JMS) is a popular Java interface to message-oriented middleware products. In JMS, security is usually delegated to the messaging provider, with clients getting security configuration as part of the administered objects they use. However, the interface provides for user identification and authentication with the method:

public Connection ConnectionFactory.createConnection(String username, String password);

This method is used to create connections to a queue manager (corresponding QueueConnectionFactory and TopicConnectionFactory methods exist for domain-specific JMS applications). By default, the authentication provided by these methods is rather limited in the WebSphere® MQ implementation of JMS. This article shows you how to use new Object Authority Manager (OAM) functionality introduced in WebSphere MQ V6 to provide more comprehensive user authentication.

Using the OAM Authenticate User function

In order to provide customised username/password authentication, you need to to implement two new authorization service functions introduced in WebSphere MQ V6. MQZ_AUTHENTICATE_USER is invoked by the queue manager to authenticate a user during connect calls, while MQZ_FREE_USER is called to free resources when an application disconnects. You also need to define an initialization entry point that calls MQZEP to register the implementations of MQZ_AUTHENTICATE_USER and MQZ_FREE_USER. All other authorization service entry points will be provided by the default OAM.

In this example, MQZ_AUTHENTICATE_USER will be implemented to only accept a user testuser with password secret (in the example code they are defined as constants USER_ID and PASSWORD). Any other username/password combination will cause the function to return MQRC_NOT_AUTHORIZED, which will cause a javax.jms.JMSSecurityException to be thrown on a JMS client. An outline of the required code is shown below; for a full implementation, you can download the sample code.


Listing 1. A simple implementation of MQZ_AUTHENTICATE_USER
// hard-code a single allowed userId and password
const char USER_ID[] = "testuser";
const char PASSWORD[] = "secret";

/****************************************************************************/
/* Simple implementation of MQZ_AUTHENTICATE_USER. This retrieves a userId
/* and password from the supplied MQCSP structure, and compares them with
/* values defined above. If they match then it returns MQCC_OK and MQRC_NONE,
/* while if they differ it returns MQCC_FAILED with MQRC_NOT_AUTHORIZED.
/****************************************************************************/
static void MQENTRY EXAMPLE_AUTHENTICATE_USER(MQCHAR48 QMgrName, PMQCSP pSecurityParms,
            PMQZAC pApplicationContext, PMQZIC pIdentityContext,
            PMQPTR pCorrelationPtr, PMQBYTE pComponentData,
            PMQLONG pContinuation, PMQLONG pCompCode, PMQLONG pReason) {

    // if authType set to MQCSP_AUTH_USER_ID_AND_PWD then we need to check the userId and password
    if (pSecurityParms -> AuthenticationType == MQCSP_AUTH_USER_ID_AND_PWD) {

        // pointer to the UserId, and user Id length
        MQPTR    pUserId        = pSecurityParms->CSPUserIdPtr;
        MQLONG   userIdLength   = pSecurityParms->CSPUserIdLength;

        // pointer to the Password, and password length
        MQPTR    pPassword      = pSecurityParms->CSPPasswordPtr;
        MQLONG   passwordLength = pSecurityParms->CSPPasswordLength;

        // do not continue with next service component
        *pContinuation = MQZCI_STOP;

        // check for null pointers to the userId and password
        if (pUserId && pPassword) {

            // check the username and password
            if (strncmp((char*)pUserId, USER_ID, userIdLength) ||
                strncmp((char*)pPassword, PASSWORD, passwordLength)) {
                // strncmp returned a non-zero value, so they don't match
                *pCompCode = MQCC_FAILED;
                *pReason = MQRC_NOT_AUTHORIZED;
            }
            else {
                // identical, return MQCC_OK
                *pCompCode     = MQCC_OK;
                *pReason       = MQRC_NONE;
            }
        }
        else {
            // either pUserId or pPassword is null and can't be checked, so return MQCC_FAILED
            *pCompCode = MQCC_FAILED;
            *pReason = MQRC_NOT_AUTHORIZED;
	    }
    }
    else {
        // authType is set to MQCSP_AUTH_NONE so no checking to do, return MQCC_OK and MQRC_NONE
        *pCompCode     = MQCC_OK;
        *pReason       = MQRC_NONE;
    }
}

The username and password are stored in a MQCSP structure, and a pointer to this structure, pSecurityParms, is passed into the function when it is called. The function first checks to see if any authentication is required, by seeing if the AuthenticationType attribute of the MQCSP structure is set to MQCSP_AUTH_USER_ID_AND_PWD. If it is, the username and password are checked against hard-coded values using the strncmp() function. If they both match, then the function returns MQCC_OK; otherwise it returns MQCC_FAILED with reason code MQRC_NOT_AUTHORIZED.

The corresponding implementation of MQZ_FREE_USER is very simple; no resources were allocated by MQZ_AUTHENTICATE_USER so it can just return MQCC_OK. If resources were allocated by the authenticate user function, they should be freed here:


Listing 2. A simple implementation of MQZ_FREE_USER
static void MQENTRY EXAMPLE_FREE_USER(MQCHAR48 QMgrName, PMQZFP pFreeParms, 
            PMQBYTE pComponentData, PMQLONG pContinuation, PMQLONG pCompCode, PMQLONG pReason) {

    // finally, simply return MQCC_OK
    *pCompCode     = MQCC_OK;
    *pReason       = MQRC_NONE;
}

Finally, it is necessary to implement an entry point to invoke the MQZEP function to add the EXAMPLE_AUTHENTICATE_USER and EXAMPLE_FREE_USER functions to the entry point vector for this service component:


Listing 3. An entry point to register EXAMPLE_AUTHENTICATE_USER and EXAMPLE_FREE_USER
void MQENTRY MQStart(MQHCONFIG hc, MQLONG Options, MQCHAR48 QMgrName, MQLONG ComponentDataLength,
            PMQBYTE pComponentData, PMQLONG pVersion, PMQLONG pCompCode, PMQLONG pReason) {

    // return values
    *pCompCode  = MQCC_OK;
    *pReason    = MQRC_NONE;
    *pVersion   = MQZAS_VERSION_5;

    // Initialise the entry points that we have defined; EXAMPLE_AUTHENTICATE_USER first
    MQZEP(hc,MQZID_AUTHENTICATE_USER,(PMQFUNC)EXAMPLE_AUTHENTICATE_USER,pCompCode,pReason);
    // if that succeeded (i.e. pCompCode isn't MQCC_FAILED), initialize EXAMPLE_FREE_USER
    if (*pCompCode == MQCC_OK) {
	    MQZEP(hc,MQZID_FREE_USER,(PMQFUNC)EXAMPLE_FREE_USER,pCompCode,pReason);
    }
    // if one of these failed, set the reason to MQRC_INITIALIZATION_FAILED
    if (*pCompCode != MQCC_OK) {
        *pReason   = MQRC_INITIALIZATION_FAILED;
    }
}

Here, two calls are made to MQZEP. The first registers the EXAMPLE_AUTHENTICATE_USER entry point, and, if that succeeds, the second call registers EXAMPLE_FREE_USER. If both calls succeed, the function returns MQCC_OK; otherwise it returns MQCC_FAILED with MQRC_INITIALIZATION_FAILED.

Building the authorization service

The following sections show you how to build and install the authorization service on Microsoft® Windows® and on UNIX®. It is assumed that WebSphere MQ has been installed and configured to use its default directory locations. If different directories were specified during installation, then you need to modify the following commands as appropriate.

Microsoft Windows

On Windows, the authorization service must be compiled to a .dll and linked with mqm.lib and mqmzf.lib. With Microsoft Visual Studio, use the procedure below. First, if necessary, set up the required paths:

C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin\vcvars32.bat

Next, compile and link with the commands

cl /MD /W3 /Zp4 /D "WIN32" /D "_WINDOWS" /c /I "C:\IBM\WebSphere MQ\Tools\c\include" 
example.c link /subsystem:windows /dll "C:\Program Files\IBM\WebSphere MQ\Tools\lib\mqm.lib"
"C:\Program Files\IBM\WebSphere MQ\Tools\lib\mqmzf.lib" example.obj /export:MQStart

Finally, copy example.dll to the WebSphere MQ bin directory (usually C:\Program Files\IBM\WebSphere MQ\bin.

UNIX platforms

On UNIX, you must link the authorization service with the shared libraries libmqm* and libmqmzf* (threading suffixes and file extensions vary by platform). The OAM runs in a threaded environment, so you must use the threaded versions of these libraries. The commands necessary to compile and link the authorization service vary depending on the platform. The following commands show how to compile and link on 32-bit Linux and 64-bit AIX. They will produce a shared library called example in the /var/mqm/exits directory:

Linux Intel (32-bit)

gcc -e MQStart -shared -I/opt/mqm/lib -L/usr/lib -lc -lnsl -ldl -lmqmzf_r
  -o /var/mqm/exits/example example.c

AIX (64-bit)

xlc_r -q64 -e MQStart -bM:SRE  -I/usr/mqm/inc -L/usr/mqm/lib64 -lmqmzf_r
  -o /var/mqm/exits/example example.c

For information on compiling the authorization service on other platforms, see the WebSphere MQ Application Programming Guide.

Installing the OAM

Microsoft Windows

You must install the authorization service by editing the Windows registry (be careful when editing the registry). Using the Windows regedit utility:

  1. Select the registry key HKEY_LOCAL_MACHINE/SOFTWARE/IBM/MQSeries/CurrentVersion/Configuration/QueueManager/.
  2. Open the appropriate QueueManager key, for example, ExampleQM.
  3. Select the ServiceComponent key.

The regedit window should now look like this:


Figure 1. Windows registry prior to adding the example authorization service
Figure 1
  1. Right-click ServiceComponent and select New => Key.
  2. Name the key example.auth.service.
  3. Left-click on the new key to select it.
  4. In the right-hand window, right-click and select New => String Value.
  5. Name the String Value Name.
  6. Right-click on the String value, select Modify, and enter the value example.auth.service.
  7. Repeat the last three steps to add three more String Values:
    1. name: Module, value: C:\Program Files\IBM\WebSphere MQ\bin\example.dll
    2. name: Service, value: AuthorizationService
    3. name: ComponentDataSize, value: 0

The regedit window should now look like this:


Figure 2. Windows registry after adding the example authorization service
Figure 2

Unix

On UNIX platforms, a stanza must be added to qm.ini. Edit the file /var/mqm/qmgrs/QMname/qm.ini, where QMname is the name of the WebSphere MQ queue manager being used, and add the following lines into the file before the default ServiceComponent stanza:

ServiceComponent:
    Name=example.auth.service
    Module=/var/mqm/exits/example
    Service=AuthorizationService
    ComponentDataSize=0

Install verification

The WebSphere MQ queue manager must be restarted before the new authorization service will be activated. After the queue manager has been restarted, installation can be verified with WebSphere MQ Explorer. Start Explorer with the strmqcfg command, then right-click on the queue manager where the Authorization Service was installed, and select Properties. Next, select Installable Services in the window that opens. The custom Authorization Service should be listed in the ServiceComponents tab:


Figure 3. Verifying installation with the MQ Explorer

JMS authentication

WebSphere MQ JMS applications can connect to a WebSphere MQ queue manager in two ways. In bindings mode, clients connect directly to WebSphere MQ using the Java Native Interface (JNI), while in client mode they connect using Transmission Control Protocol/Internet Protocol (TCP/IP). The way in which the authentication service is used differs depending on which mode is selected.

Authentication in bindings mode

When bindings connections are used, the JMS username and password are passed directly to the OAM authenticate user function for authentication. If the supplied credentials are rejected, then a javax.jms.SecurityException will be thrown. With the example authorization service described above, the following code fragment:


Listing 4. Connecting to a queue manager with an invalid username and password
import javax.jms.*;

public class BindingsExample {

ConnectionFactory cf = null;
Connection conn = null;

    public static void main(String[] args) {

        try {
            //configure JNDI context
            Context ctx = new InitialDirConext(...);
            // get a pre-defined connection factory from JNDI
            cf = (ConnectionFactory)ctx.lookup("ExampleCF");
            // connect
            conn = cf.createConnection("myUser","myPassword");
        }
        catch (Exception e) {
            //process JMSException/NamingException etc.
        }
    }
}

will result in the exception:

javax.jms.JMSSecurityException: MQJMS2013: 
invalid security authentication supplied for MQQueueManager

If the createConnection(...) method call is replaced with

conn = cf.createConnection("testuser","secret");

then the Connection will be created successfully.

Authentication in client mode

Authentication in client mode is slightly more complex. By default, the JMS client will not send an MQCSP structure to a queue manager when it connects in client mode. If the authorization service is to be used, this structure must be defined in a client security exit:

  1. When the exit is invoked with the exit reason MQXR_SEC_PARMS, the exit creates a new MQConnectionSecurityParameters object.
  2. The exit gets the username and password supplied to the JMS createConnection(String username, String password) method from the fields channelDefParms.remoteUserId and channelDefParms.remotePassword, where channelDefParms is the MQChannelExit object passed to the exit when it is invoked.
  3. The exit modifies the userId and Password if required (for example, to hash the password for transmission; in this case the authorization service would also have to be coded to use a hashed password) and sets them in the MQConnectionSecurityParameters object using the methods setCSPUserId(String UserId) and setCSPPassword(String password).
  4. The exit sets the authenticationType field to MQC.MQCSP_AUTH_USER_ID_AND_PWD using the method setAuthenticationType(int authType).
  5. Finally, the exit sets the MQConnectionSecurityParameters object into the MQChannelExit object using the method setMQCSP(MQConnectionSecurityParameters csp):

Listing 5. A simple Security exit fragment that creates a MQConnectionSecurityParameters object
package example;

public class SecurityExit implements com.ibm.mq.MQSecurityExit {

    public byte[] securityExit(MQChannelExit channelExitParms,
                               MQChannelDefinition channelDefParms,
                               byte[] agentBuffer) {

        switch (channelExitParms.exitReason) {

        case MQC.MQXR_INIT:
            // initialize etc.
            break;

        case MQC.MQXR_INIT_SEC:
            // transmit MCAUserId if desired etc.
            break;

        case MQC.MQXR_SEC_PARMS:
            // create a new MQCSP object
            MQConnectionSecurityParameters csp = new MQConnectionSecurityParameters();
            // set authenticationType to AUTH_USER_ID_AND_PWD
            csp.setAuthenticationType(MQC.MQCSP_AUTH_USER_ID_AND_PWD);
            // set CSP UserId and password from the JMS-supplied values
            csp.setCSPUserId(channelDefParms.remoteUserId.trim());
            csp.setCSPPassword(channelDefParms.remotePassword.trim());
            // finally add MQCSP object to the channelExitParms
            channelExitParms.setMQCSP(csp);
            break;

         case MQC.MQXR_TERM:
            // etc.
        }

        return agentBuffer;
    }
}

If MQChannelExit.getMQCSP() is non-null when the exit returns after being invoked with MQXR_SEC_PARMS, then the contents of the MQConnectionSecurityParameters object defined by the exit will be transmitted to the queue manager and passed to the authorization service for authentication.

Two additional configuration steps are necessary. Firstly, the supplied CSPUserId must correspond to a user on the target server. Secondly, it may be necessary to set the MCAUSER property on the channel being used to a user with authorization to access queue manager resources. In a full security solution, this would usually be set by the client security exit when invoked with MQXR_INIT_SEC, but it may also be set with WebSphere MQ Explorer, or with the runmqsc command:

alter channel(channel name) chltype(svrconn) mcauser(UserId)

If the ConnectionFactory is defined in a JNDI namespace, the exit can be set using the JMSAdmin tool with the command:

ALTER CF(CFname) SECEXIT("example.SecurityExit")

The ConnectionFactory must be set to use TRANSPORT(CLIENT); otherwise this command will fail. If the ConnectionFactory is defined programmatically, the exit can be set as follows:

ConnectionFactory cf = new MQConnectionFactory();
cf.setSecurityExit("example.SecurityExit");

Creating a Connection with this ConnectionFactory will now use the example authentication service to verify the supplied username and password.

Conclusion

This article has shown how to implement and deploy a custom authentication service, and how to use it from JMS applications. The example is simple, but includes all the elements necessary to provide customised authorization for connection requests from a WebSphere MQ JMS client.

Acknowledgements

The author would like to thank Eileen Dreyer for reading the manuscript, and David Postlethwaite for frequent advice about the workings of the Object Authority Manager.



Download

DescriptionNameSizeDownload method
Code sample in zip formatexample.zip2 KBFTP|HTTP

Information about download methods


Resources

About the author

Ben Ritchie is a software engineer at the IBM Hursley Software Lab in the UK, and he works as a Java developer on the WebSphere MQ Client Development team. Ben joined IBM in 2001 after completing a DPhil in astrophysics at the University of Sussex, and has worked with JMS ever since, first with WebSphere MQ Everyplace and more recently with WebSphere MQ. You can contact Ben at ben.ritchie@uk.ibm.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=100659
ArticleTitle=Enhancing WebSphere MQ JMS security using the Object Authority Manager
publish-date=12142005
author1-email=ben.ritchie@uk.ibm.com
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Try IBM PureSystems. No charge.

Special offers