MQ

MQ

Join this online group to communicate across IBM product users and experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.

 View Only
Expand all | Collapse all

Channel Security Exit Hconn Question

  • 1.  Channel Security Exit Hconn Question

    Posted 9 days ago
    Edited by Tim Zielke 9 days ago

    If I am working with a channel security exit like this:

    DllExport void MQENTRY ChlExit (PMQVOID pChannelExitParms,
                            PMQVOID pChannelDefinition,
                            PMQLONG pDataLength,
                            PMQLONG pAgentBufferLength,
                            PMQVOID pAgentBuffer,
                            PMQLONG pExitBufferLength,
                            PMQPTR  pExitBufferAddr)
    {
        PMQCXP pParms = (PMQCXP)pChannelExitParms;
        PMQCD  pCD    = (PMQCD)pChannelDefinition;

        /* return if no addressability to input pointers we are using in exit, probably not needed but be defensive */
        if (pParms == NULL || pCD == NULL)
           return;

        /* Only MQPUT1 when we have a security exit being called because of MQXR_SEC_PARMS */
        /* Also make sure MQCXP version is >= 9, since we rely on values up until version 9 in this exit */
        if(pParms->ExitId == MQXT_CHANNEL_SEC_EXIT && pParms->ExitReason == MQXR_SEC_PARMS && pParms->Version >= 9)
        {

    and I later want to do an MQPUT1 with the Hconn that is in the passed in MQCXP. I have a few questions about working with this Hconn.

    1) Can I safely assume since I am at least at version 9 of the MQCXP that I will be provided a valid Hconn and just use it directly into the MQPUT1 call? For example:

            pParms->pEntryPoints->MQPUT1_Call(pParms->Hconn,  /* connection handle  */
                                              &od,            /* object descriptor  */
                                              &md,            /* message descriptor */
                                              &pmo,           /* default options    */
                                              messlen,        /* message length     */
                                              messageBuffer,  /* message buffer     */
                                              &CompCode,      /* completion code    */
                                              &Reason);       /* reason code        */

    2) Or should I be defensive and not assume the Hconn is valid (e.g. 0) and do something like the following? High level, get my own connection and disconnect, assuming I was not returned an already active connection.

            /* PUT1 our message data to a queue */
            MQHCONN  Hcon;                  /* connection handle */
            MQOD    od = {MQOD_DEFAULT};    /* Object Descriptor */
            MQMD    md = {MQMD_DEFAULT};    /* Message Descriptor */
            MQPMO   pmo = {MQPMO_DEFAULT};  /* put message options */
            MQLONG  CompCode;               /* completion code */
            MQLONG  Reason;                 /* reason code */
            MQLONG  messlen;                /* message length received */

            int needMQDISC = 0;
            if (pParms->Hconn > 0)
            {
               Hcon = pParms->Hconn;
            }
            else
            {
               char QMName[50];               /* queue manager name */
               QMName[0] = 0;                 /* default */
               MQCNO cno = {MQCNO_DEFAULT};   /* connection options */
               pParms->pEntryPoints->MQCONNX_Call(QMName,    /* queue manager      */
                                                  &cno,      /* connection options */
                                                  &Hcon,     /* connection handle  */
                                                  &CompCode, /* completion code    */
                                                  &Reason);  /* reason code        */
               /* Do not MQDISC if already connected */
               if (CompCode == 1 && Reason == 2002)
               {
               }
               else
               {
                  needMQDISC = 1;
               }
            }

            pmo.Options = MQPMO_NO_SYNCPOINT; /* No syncpoint */
            strncpy(od.ObjectName, "TCZ.QUEUE", MQ_Q_NAME_LENGTH);
            messlen  = mbOffset;            /* length of message */

            pParms->pEntryPoints->MQPUT1_Call(Hcon,           /* connection handle  */
                                              &od,            /* object descriptor  */
                                              &md,            /* message descriptor */
                                              &pmo,           /* default options    */
                                              messlen,        /* message length     */
                                              messageBuffer,  /* message buffer     */
                                              &CompCode,      /* completion code    */
                                              &Reason);       /* reason code        */

            if (needMQDISC)
            {
                pParms->pEntryPoints->MQDISC_Call(&Hcon,     /* connection handle */
                                                  &CompCode, /* completion code   */
                                                  &Reason);  /* reason code       */
            }

    One reason I am asking is that I have a found a scenario (channel security exit on a CLUSRCVR channel with an exit reason of MQXR_INIT_SEC) where the amqrmppa process hits a SIGSEGV and the queue manager becomes unstable when you pass the pParms->Hconn (which I have validated is a valid Hconn value) directly into the MQPUT1 call. What is interesting is that if you instead define an MQHCONN Hconn variable, assign Hconn = pParms->Hconn, and then call the MQPUT1 with the Hconn variable, the exit works fine. But this instability got me thinking if I should be more defensive in my coding in working with this pParms->Hconn.



    ------------------------------
    Tim Zielke
    ------------------------------



  • 2.  RE: Channel Security Exit Hconn Question

    Posted 8 days ago

    You are not supposed to need to do that. If you are passed an hConn, it should either be something you can use or MQHC_UNUSABLE_HCONN (-1). It certainly shouldn't be something that, if used, causes a SIGSEGV. Open a case on that one.

    Cheers,
    Morag



    ------------------------------
    Morag Hughson
    MQ Technical Education Specialist
    MQGem Software Limited
    Website: https://www.mqgem.com
    ------------------------------



  • 3.  RE: Channel Security Exit Hconn Question

    Posted 8 days ago
    Edited by Tim Zielke 8 days ago

    Thank you for the reply! Just curious though. If you did want to code the edit to handle the MQHC_UNUSABLE_HCONN use case (or for some unforeseen reason an invalid Hconn) and also have logic to create your own connection for the MQPUT1, does that logic in #2 above look reasonable to you as an option?



    ------------------------------
    Tim Zielke
    ------------------------------



  • 4.  RE: Channel Security Exit Hconn Question

    Posted 8 days ago

    I understand that you are in a situation where you have to work around the SIGSEGV and can't use the provided hConn. So for this situation the code you show seems fair enough. It is worth remembering though, that now you are adding an MQCONN+MQDISC to the cost of each exit invocation which is certainly not ideal. You might want to consider obtaining an area of memory, holding the hConn there, and storing that away in MQCXP.ExitUserArea which will then be given back to you on each subsequent call. You can then MQDISC when you are all done. Can you tell when you are all done? Reason I ask is that the normal time to tidy things up is MQXR_TERM, but that comes at channel end, and for a security exit you're all done once the channel starts up, so you probably don't want to leave your hConn around for the life of the channel. You can see why it is better, when you can, to use the supplied hConn!

    Cheers,
    Morag



    ------------------------------
    Morag Hughson
    MQ Technical Education Specialist
    MQGem Software Limited
    Website: https://www.mqgem.com
    ------------------------------



  • 5.  RE: Channel Security Exit Hconn Question

    Posted 6 days ago

    Tim,

    Call me confused here. I'd understand you doing something with the message on a message exit, why would you want to do a put1 on the SECURITY exit?



    ------------------------------
    Francois Brandelik
    ------------------------------



  • 6.  RE: Channel Security Exit Hconn Question

    Posted 6 days ago

    Hi Francois,

    This exit is following the pattern described in this connwarn exit here -> https://github.com/ibm-messaging/mq-exits/tree/master/channel/connwarn

    I am enhancing it to also capture the client SSLPEER and SSLCERTI of a TLS channel. This is being done so we can do things like track if any client certificates are about to expire.

    Thanks,

    Tim



    ------------------------------
    Tim Zielke
    ------------------------------



  • 7.  RE: Channel Security Exit Hconn Question

    Posted 6 days ago

    In regards to opening a case on this issue, I tried to do that today but when I went to recreate the issue, I could no longer do that. The server has been rebooted since I was last able to recreate the issue, so maybe it has something to do with how shared object files are now getting assigned to the amqrmppa address space or something to that effect. It was a very odd issue to start with. I am going to paste some of the FDC information from the SIGSEGV, in case that stands out to anyone.

    | Program Name      :- amqrmppa                                               |
    | Probe Description :- AMQ6109S: An internal IBM MQ error has occurred.       |
    | FDCSequenceNumber :- 0                                                      |
    | Arith1            :- 11 (0xb)                                               |
    | Comment1          :- SIGSEGV: address not mapped(0x780)                     |

    O/S Call Stack for current thread
    Exception

    MQM Function Stack
    rriCallerThread
    rriCallerEntry
    rriFreeSess
    rriTermExits
    rriTermExit
    xcsUnloadFunction
    xcsFFST

    -{ rriTermExits
    --{ lpiSPIAlter
    ---{ zstGetPCD
    ----{ zstVerifyPCD
    ----} zstVerifyPCD rc=OK
    ---} zstGetPCD rc=OK
    ---{ xcsCheckPointer
    ---} xcsCheckPointer rc=OK
    --} lpiSPIAlter rc=OK
    --{ lpiSPIStoreSharedHConn
    ---{ zstGetPCD
    ----{ zstVerifyPCD
    ----} zstVerifyPCD rc=OK
    ---} zstGetPCD rc=OK
    --} lpiSPIStoreSharedHConn rc=OK
    --{ xcsIsEnvironment
    --} xcsIsEnvironment rc=OK
    --{ rriTermExit
    ---{ rriCALL_EXIT
    ---} rriCALL_EXIT rc=OK
    --} rriTermExit rc=OK
    --{ rriTermExit
    ---{ xcsUnloadFunction
    ----{ xcsIsEnvironment
    ----} xcsIsEnvironment rc=OK
    ----{ xcsFFST



    ------------------------------
    Tim Zielke
    ------------------------------



  • 8.  RE: Channel Security Exit Hconn Question

    Posted 6 days ago

    Hi Tim,

    > 1) Can I safely assume since I am at least at version 9 of the MQCXP that I will be provided a valid Hconn and just use it directly into the MQPUT1 call? For example:

    Here's the code I use in my channel security exits:

    #if defined(MQCXP_VERSION_7)
       if ( (pCXP->Version >= MQCXP_VERSION_7)  &&
            (pCXP->Hconn != MQHC_UNUSABLE_HCONN) )
       {
          /* Grab & save the handle. */
          Hconn = pCXP->Hconn;
       }
       else
    #endif
       {
          /* Connect to the local queue manager */
          rcode = ConnectToQMgr(&Hconn,
                                &connectRC,
                                pSX->QMgrName,
    #if defined(MQ_7_MI)
                                pCXP->pEntryPoints,
    #endif
                                pSX->pLFS);
       }

    That piece of code works pretty much on every version of MQ.

    <Vendor_Plug>

    May I suggest that you have a look at Capitalware's MQ Channel Connection Inspector. It does precisely what you are trying to create with your channel security exit. i.e. Why re-invent the wheel?

    </Vendor_Plug>

    later

    Roger



    ------------------------------
    Roger Lacroix
    CTO
    Capitalware Inc.
    London Canada
    https://capitalware.com
    ------------------------------



  • 9.  RE: Channel Security Exit Hconn Question

    Posted 5 days ago

    Hi Roger,

    Thanks for the code snippet!

    Thanks,

    Tim



    ------------------------------
    Tim Zielke
    ------------------------------



  • 10.  RE: Channel Security Exit Hconn Question

    Posted 5 days ago

    It's generally safer to be defensive. Even if pParms->Hconn is valid, using a local copy like you described can prevent issues like the SIGSEGV you saw. Checking the handle before use and falling back to a new connection if needed adds stability, especially across different exit reasons and channel types.



    ------------------------------
    Michel David
    ------------------------------



  • 11.  RE: Channel Security Exit Hconn Question

    Posted 5 days ago

    I think I found the issue with the SIGSEGV. This seems to be the flow that triggers it. This is being done on a Linux queue manager at 9.4.0.16.

    1) You have a channel security exit (e.g. clientcert) that you are using that is making an MQI call (e.g. MQPUT1) to the queue manager. clientcert is located in /var/mqm/exits64.

    2) You then copy a new version of clientcert to /var/mqm/exits64.

    3) When you execute the new version of clientcert, the amqrmppa process crashes with a SIGSEGV.

    When I look at the pmap of the amqrmppa address space after #1 and compare it to #3, it looks like the original exit was using some shared memory segments (probably due to the local connection to the queue manager) that are now deleted when I look at the pmap of the amqrmppa in the FDC that has the SIGSEGV. Not sure if that is the underlying issue, but this is how the issue is recreated.



    ------------------------------
    Tim Zielke
    ------------------------------



  • 12.  RE: Channel Security Exit Hconn Question

    Posted 4 days ago

    That sounds plausible. Especially if your exit is doing its own MQCONN (did you do a matching MQDISC on channel terminate?) 

    Whenever I'm developing exits, I always restart the qmgr. And I don't actually put the new code version into the exits directory until the qmgr has stopped. Just to make sure. Different operating systems can behave in strange ways when you try to overwrite an in-use library, or try to load the same named file into a process. I've seen too many weird things to try to short-cut it.

    There was an environment variable used around MQv5 timeframe - set AMQ_INHIBIT_DLCLOSE=true - that modified some of the dynamic load behaviour. Not needed these days, but I wonder if it might have had some effect here. Not necessarily positive ones, but it's possible that it might even have stopped you overwriting the exit until the qmgr was ended. Again, some of this tends to be OS-specific.



    ------------------------------
    Mark Taylor
    Winchester
    ------------------------------



  • 13.  RE: Channel Security Exit Hconn Question

    Posted 4 days ago
    Edited by Tim Zielke 4 days ago

    For the example above, the original clientcert exit was just using the passed in Hconn from the MQCXP and only did an MQPUT1 for any MQI calls. However, there were long running client connections into the queue manager that would have invoked that original clientcert exit and were still running when the new cliencert exit update was copied into /var/mqm/exits64 and then later invoked (which caused the SIGSEGV in the amqrmppa process that the exit is running under). I will make sure going forward to not make any exit changes unless the queue manager is stopped, especially for changes to existing exits. 



    ------------------------------
    Tim Zielke
    ------------------------------



  • 14.  RE: Channel Security Exit Hconn Question

    Posted 4 days ago

    Just curious. Would the same approach be recommended for z/OS IBM MQ exits? For example, if you already have a loaded exit in your CHIN from CSQXLIB, and you want to update that exit, would it be recommended to first stop the queue manager before updating that same exit in the CSQXLIB dataset?



    ------------------------------
    Tim Zielke
    ------------------------------



  • 15.  RE: Channel Security Exit Hconn Question

    Posted 3 days ago

    Hi Tim,

    Copying over the top of a DLL in Linux that is still loaded and being used is not advisable. Stopping the Queue Manager, while absolute, is probably more heavyweight than needed. Just make sure that all running channels are stopped. I tend to develop exits with a runmqlsr in the foreground with environment variable MQNOREMPOOL set to yes so I can fully control where the channels run, and also make use of printfs that I can actually see while developing.

    Mark is quite right that the OSes vary hugely in this regard though. On Windows you can't get into the pickle you have because the copy would fail if it was currently loaded. On z/OS you can't get into the pickle you have because a new copy is loaded into memory each time a new version exists. No need to stop the queue manager. Also, even if you did need to cycle something, you only need to stop the CHIN address space for channel exits on z/OS.

    Given that you know the SIGSEGV is as a result of the DLL overlay, I would argue against making a new hConn, unless you have some other reason to do it. Not sure exactly what your exit is doing, but it sounds like you are making a security exit, so the fact that MQPUT1s would be in the same batch as a channel messages is not quite so much of an issue - I assume you are doing an MQPMO_NO_SYNCPOINT put? If you're intending to code this exit on z/OS as well, remember to be explicit about that!

    btw - is there any chance you are writing a channel exit version of the recently provided positive authority/authentication events? They capture inbound SSLPEER and SSLCERTI. See example in Using MQ Feature - Positive Authority Events

    Cheers,
    Morag



    ------------------------------
    Morag Hughson
    MQ Technical Education Specialist
    MQGem Software Limited
    Website: https://www.mqgem.com
    ------------------------------



  • 16.  RE: Channel Security Exit Hconn Question

    Posted 3 days ago

    Thanks for all the helpful information there! Yes, I am writing an exit for needs that the 9.4.3 Positive Authority Events will cover. Wasn't aware of that new feature, so thanks for the heads up on that. We follow the LTS roll out, so I may have use of the exit until we get this new functionality in the next version of IBM MQ. I ended landing on this Hconn logic for my exit. It will more than likely just use the passed in Hconn in the MQCXP.

            /* PUT1 our data to the MQADMIN.CLIENT.CERT.QUEUE */
            MQHCONN  Hcon;                  /* connection handle */
            MQOD    od = {MQOD_DEFAULT};    /* Object Descriptor */
            MQMD    md = {MQMD_DEFAULT};    /* Message Descriptor */
            MQPMO   pmo = {MQPMO_DEFAULT};  /* put message options */
            MQLONG  CompCode;               /* completion code */
            MQLONG  Reason;                 /* reason code */
            MQLONG  messlen;                /* message length received */

            int needMQDISC = 0;
            if (pParms->Hconn != MQHC_UNUSABLE_HCONN)
            {
               Hcon = pParms->Hconn;
            }
            else
            {
               char QMName[50];               /* queue manager name */
               QMName[0] = 0;                 /* default */
               MQCNO cno = {MQCNO_DEFAULT};   /* connection options */
               pParms->pEntryPoints->MQCONNX_Call(QMName,    /* queue manager      */
                                                  &cno,      /* connection options */
                                                  &Hcon,     /* connection handle  */
                                                  &CompCode, /* completion code    */
                                                  &Reason);  /* reason code        */
               if (CompCode == MQCC_FAILED)
               {
                  Hcon = MQHC_UNUSABLE_HCONN;
               }
               else
               {
                  /* Do not MQDISC if already connected */
                  if (CompCode == 1 && Reason == 2002)
                  {
                  }
                  else
                  {
                     needMQDISC = 1;
                  }
               }
            }

            if (Hcon != MQHC_UNUSABLE_HCONN)
            {
               pmo.Options = MQPMO_NO_SYNCPOINT; /* No syncpoint */
               strncpy(od.ObjectName, "MQADMIN.CLIENT.CERT.QUEUE", MQ_Q_NAME_LENGTH);
               messlen  = mbOffset;            /* length of message */

               pParms->pEntryPoints->MQPUT1_Call(Hcon,           /* connection handle  */
                                                 &od,            /* object descriptor  */
                                                 &md,            /* message descriptor */
                                                 &pmo,           /* default options    */
                                                 messlen,        /* message length     */
                                                 messageBuffer,  /* message buffer     */
                                                 &CompCode,      /* completion code    */
                                                 &Reason);       /* reason code        */
               if (needMQDISC)
               {
                   pParms->pEntryPoints->MQDISC_Call(&Hcon,     /* connection handle */
                                                     &CompCode, /* completion code   */
                                                     &Reason);  /* reason code       */
               }
            }



    ------------------------------
    Tim Zielke
    ------------------------------



  • 17.  RE: Channel Security Exit Hconn Question

    Posted 4 days ago

    Based on your example, the safest approach is to first assign pParms->Hconn to a local Hconn variable before using it in MQPUT1. Check if Hconn is valid, and if not, establish your own connection. After the put operation, disconnect only if you created a new connection. This ensures stability and avoids issues like SIGSEGV in channel security exits."



    ------------------------------
    Michel David
    ------------------------------



  • 18.  RE: Channel Security Exit Hconn Question

    Posted 2 days ago

    Hi Tim,

    Thank you for sharing your detailed scenario and code examples. From my understanding, the core issue revolves around ensuring safe use of the Hconn from the MQCXP structure when performing an MQPUT1 call within a channel security exit. It seems that even though the Hconn is valid, passing it directly can sometimes cause instability depending on the context, while assigning it to a local variable first avoids the problem.

    Your defensive approach checking the validity of Hconn and conditionally establishing a new connection if needed appears to be the safest strategy, especially in complex environments like CLUSRCVR channels or different exit reasons. This ensures stability while maintaining the intended functionality. It also highlights that subtle differences in handling connection handles can have a significant impact on the queue manager's behavior.

    It may be a good practice to always isolate the connection handle in a local variable and validate it before performing MQPUT1 operations to prevent potential crashes or undefined behavior.

    Thanks again for the clear explanation and code samples they make the scenario much easier to follow.



    ------------------------------
    Michel David
    ------------------------------



  • 19.  RE: Channel Security Exit Hconn Question

    Posted 2 days ago

    Hi Michel,

    For the avoidance of any doubt, let's just be clear here. The SIGSEGV was as a result of copying a new version of the exit DLL over the top of one that was currently in use and loaded in other channels - something that Linux unfortunately let's you do. The hConn provided by the queue manager to the exit should be used unless you want to avoid being part of the channel batch. Assigning it to a local variable or otherwise is not related to the issue. It is not necessary to "isolate the hConn value into a local variable before using it".

    I don't want future readers to get totally the wrong idea!

    Cheers,
    Morag



    ------------------------------
    Morag Hughson
    MQ Technical Education Specialist
    MQGem Software Limited
    Website: https://www.mqgem.com
    ------------------------------



  • 20.  RE: Channel Security Exit Hconn Question

    Posted yesterday
    Edited by Tim Zielke yesterday

    Yes, what Morag is saying is correct. If I had only been updating exit changes after I stopped the queue manager, just doing only the following for the MQI coding would have worked.

              pParms->pEntryPoints->MQPUT1_Call(pParms->Hconn,           /* connection handle  */
                                                 &od,            /* object descriptor  */
                                                 &md,            /* message descriptor */
                                                 &pmo,           /* default options    */
                                                 messlen,        /* message length     */
                                                 messageBuffer,  /* message buffer     */
                                                 &CompCode,      /* completion code    */
                                                 &Reason);       /* reason code        */

    However, where I ended up with the MQI coding is better as it is more defensive and has recovery capabilities in case the pParms->Hconn is unusable.

    On a separate note, I also coded this exit as a C exit on the z/OS side. On that side you code the MQI call differently.

    On z/OS the MQI call looks like this:

    MQPUT1(

    instead of this:

    pParms->pEntryPoints->MQPUT1_Call(

    What is confusing, is that using the second coding option compiles and links find on z/OS. However, when you run the exit it just stops working when it gets to the MQI call. There are also no error messages in the CHIN or MSTR. I spent some time working through that, so thought I would pass it along, too.



    ------------------------------
    Tim Zielke
    ------------------------------