Here are some things that MQ Exit developers need to be aware of:
1. Most people have never developed and will never develop an MQ Exit. Developing a MQ Exit is an ‘advanced topic’ – hence, you SHOULD have experience developing Windows DLLs or Unix shared Libraries and understand the concept of parallel processing (i.e. threading). Test, search, test again, search again. Google is now man’s best friend. USE IT.
2. Windows XP, 7, 2003 and 2008 have restrictions on system accounts (MUSR_MQADMIN is a system account!!!) for what files or directories that account can access ( i.e. On WinXP, the Exit cannot write to C:\ ) So, if in your exit, the code is writing to C:\myexit.log, and you have a problem then TRY ANOTHER DIRECTORY (even try another file name).
3. MQ Exit Library names are CASE SENSITIVE.
- e.g.
C:\Program Files\IBM\MQ\Exits64\testexit(SECEXIT)
is NOT the same as
C:\Program Files\IBM\MQ\Exits64\testexit(SecExit)
4. Do NOT write a large convoluted exit before testing it. Start with a VERY SIMPLE EXIT and then move to a more complex exit.
5. Before you post a question (on MQ ListServer or mqseries.net) about your MQ security exit, make sure you have tested the following VERY, VERY BASIC MQ security exit. Note: If you cannot get this to work, you should give your MQ exit development project to someone else (Do not post why or complain!!).
You can download the source code from here. Copy the following code to a file and call the file: testexit.c
/** * TestExit for debugging MQ exit issue. * * Location of: * Unix/Linux: /var/mqm/exits64/testexit(SecExit) * * Windows DLL: C:\Program Files\IBM\MQ\Exits64\testexit(SecExit) * * MQSC command for Unix/Linux queue manager: * DEFINE CHANNEL ('APP.CH01') CHLTYPE(SVRCONN) + * TRPTYPE(TCP) + * SENDEXIT('/var/mqm/exits64/testexit(SecExit)') + * SENDDATA(' ') + * REPLACE * * MQSC command for Windows queue manager: * DEFINE CHANNEL ('APP.CH01') CHLTYPE(SVRCONN) + * TRPTYPE(TCP) + * SENDEXIT('C:\Program Files\IBM\MQ\Exits64\testexit(SecExit)') + * SENDDATA(' ') + * REPLACE */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #if defined (_WIN32) #include <windows.h> #include <lm.h> #endif #include <cmqc.h> #include <cmqxc.h> #if defined (_WIN32) __declspec (dllexport) void MQENTRY SecExit(PMQCXP pChannelExitParms, PMQCD pChannelDefinition, PMQLONG pDataLength, PMQLONG pAgentBufferLength, PMQBYTE pAgentBuffer, PMQLONG pExitBufferLength, PMQPTR pExitBufferAddr); #else extern void MQENTRY MQStart(void) {;} #endif void MQENTRY SecExit( PMQCXP pChannelExitParms, PMQCD pChannelDefinition, PMQLONG pDataLength, PMQLONG pAgentBufferLength, PMQVOID pAgentBuffer, PMQLONG pExitBufferLength, PMQPTR pExitBufferAddr) { char outBuff[512]; char tempChl[ MQ_CHANNEL_NAME_LENGTH + 1]; FILE* fh; struct tm *newtime; time_t tclock; char *timeBuff; PMQCXP pParms = pChannelExitParms; PMQCD pChDef = pChannelDefinition; // Attention newbie user: Make sure you select a valid directory and filename!!! // fh = fopen("C:\\Program Files\\IBM\\MQ\\Exits64\\testexit.log", "a+"); fh = fopen("C:\\Temp\\testexit.log", "a+"); time( &tclock ); newtime = localtime( &tclock ); timeBuff = asctime(newtime); timeBuff[strlen(timeBuff) - 1] = 0x00; fprintf(fh, "%s : Now entering the security exit.\n", timeBuff); memcpy(tempChl, pChannelDefinition->ChannelName, MQ_CHANNEL_NAME_LENGTH); tempChl[MQ_CHANNEL_NAME_LENGTH] = 0x00; fprintf(fh, "%s : Channel name is %s\n", timeBuff, tempChl); if (pParms->ExitId == MQXT_CHANNEL_SEC_EXIT) { switch (pParms->ExitReason) { case MQXR_INIT: pParms->ExitResponse = MQXCC_OK; fprintf(fh, "%s : MQXR_INIT - Channel Initialization\n", timeBuff); break; case MQXR_INIT_SEC: pParms->ExitResponse = MQXCC_OK; fprintf(fh, "%s : MQXR_INIT_SEC - Initialize Security\n", timeBuff); break; case MQXR_SEC_MSG: pParms->ExitResponse = MQXCC_OK; fprintf(fh, "%s : MQXR_SEC_MSG - Security Message\n", timeBuff); break; case MQXR_SEC_PARMS: pParms->ExitResponse = MQXCC_OK; fprintf(fh, "%s : MQXR_SEC_PARMS - Security Parameters\n", timeBuff); break; case MQXR_TERM: pParms->ExitResponse = MQXCC_OK; fprintf(fh, "%s : MQXR_TERM - Channel Terminating\n", timeBuff); break; default: pParms->ExitResponse = MQXCC_SUPPRESS_FUNCTION; fprintf(fh, "%s : ERROR - Unknown Exit Reason\n", timeBuff); break; } } else { pParms->ExitResponse = MQXCC_SUPPRESS_FUNCTION; fprintf(fh, "%s : ERROR - Not invoked by a security exit.\n", timeBuff); return; } fprintf(fh, "%s : Now exiting the security exit.\n\n", timeBuff); fclose(fh); return; }
6. Read the IBM MQ Intercommunication manual for how to compile the security exit (e.g. testexit.c) for your particular platform.
7. Here is the channel definition for this MQ security exit to run on Windows:
DEFINE CHANNEL ('TEST.EXIT') CHLTYPE(SVRCONN) + TRPTYPE(TCP) + SCYEXIT('C:\Program Files\IBM\MQ\Exits64\testexit(SecExit)') + SCYDATA(' ') + REPLACE
8. Consult the following web page that contains several security exits that may help you out:
https://www.capitalware.com/mq_code_c.html#exitcode
9. Buy vs Build: You and your management should seriously review the costs associated with building your own security exit / solution. A company can easily spend $50,000 to $100,000 or more building an MQ security solution but they could have spent a fraction of the money if they would have purchased Capitalware’s MQ Authenticate User Security Exit or MQ Standard Security Exit. Also, who will support the internally build custom solution in years to come? Food for thought.
10. Last but not least: Read The Manual (RTM). In particular read IBM MQ Programming Guide, IBM MQ Programming Reference, IBM MQ Intercommunication and IBM MQ Messages and Codes manuals
Regards,
Roger Lacroix
Capitalware Inc.