Why do I get MQJE001: Completion Code '2', Reason '2035' after upgrading from java client 6.0.2.2 to 9.3.1.0? - ibm-mq

I work on a Java application which uses com.ibm.mqjms version 6.0.2.2. I just upgraded to com.ibm.mq.allclient version 9.3.1.0.
After the upgrade, all attempts to put a message on the queue results the following error :
com.ibm.mq.MQException: MQJE001: Completion Code '2', Reason '2035'.
The error message given by queue manager is :
AMQ9557E: Queue Manager User ID initialization failed for '[username]'.
EXPLANATION:
The call to initialize the User ID '[username]' failed with CompCode 2 and Reason
2035. If an MQCSP block was used, the User ID in the MQCSP block was ''. If a
userID flow was used, the User ID in the UID header was '' and any CHLAUTH
rules applied prior to user adoption were evaluated case-sensitively against
this value.
ACTION:
Correct the error and try again.
I've tried to add the following in the java application to disable MQCSP, but this did not help.
Hashtable props = new Hashtable();
props.put(MQConstants.USER_ID_PROPERTY, false);
props.put(MQConstants.PASSWORD_PROPERTY, false);
props.put(MQConstants.USE_MQCSP_AUTHENTICATION_PROPERTY, Boolean.FALSE);
this.queueManager = new MQQueueManager(getManager(), props);
What can I do to troubleshoot this issue? Since the issue only exists with the more recent version of the MQ java client, I'd think the issue is caused by the client?
AMQERR01.LOG
2023-01-03 13:28:41 - Process(2838.71) User(mqm) Program(amqrmppa)
Host([HOSTNAME]) Installation(Installation1)
VRMF(9.2.5.0) QMgr(TSTMQ01)
Time(2023-01-03T12:28:41.959Z)
ArithInsert1(2) ArithInsert2(2035)
CommentInsert1([USERNAME])
AMQ9557E: Queue Manager User ID initialization failed for '[USERNAME]'.
EXPLANATION:
The call to initialize the User ID '[USERNAME]' failed with CompCode 2 and Reason
2035. If an MQCSP block was used, the User ID in the MQCSP block was ''. If a
userID flow was used, the User ID in the UID header was '' and any CHLAUTH
rules applied prior to user adoption were evaluated case-sensitively against
this value.
ACTION:
Correct the error and try again.

To get the prior v6 behavior set the java system property user.name to an empty value, for example on the command line add this:
-Duser.name=
or in your program add this:
System.setProperty("user.name", "")
Background:
The IBM MQ classes for Java and IBM MQ classes for JMS are the only libraries where IBM allows you to set the username that is passed to MQ in the protocol field called RemoteUserIdentifier. This has been in place since the Java and JMS classes were released. IBM has stated this is because a java app does not have direct access to the user the process runs under only the java system property user.name which will be filled in by the JVM with a username but can be overridden as a java system property. On the other hand C, C++, .NET and XMS MQ client APIs all pass the user that the process is running as in the RemoteUserIdentifier field and you cannot override this and specify an arbitrary value.
Java and JMS also were the only APIs to allow you to specify a password value and this would be sent to the queue manager in the RemotePassword field. The RemoteUserIdentifier/RemotePassword fields are part of the MQCD (MQ channel defination) that the client sends to the queue manager when connecting.
Prior to v8 the queue manager had no built in functionality to authenticate any username or password value that was provided, you would need a security exit to get any additional validation. If you configure your MQ SVRCONN channel to allow connections as any ID with no protection (Blank MCAUSER and no security exits or SSLCIPH/SSLPEER to restrict access, then anyone can write a simple Java or JMS MQ application to assert any user value and get access to what that user would have access to on MQ, even without java someone with admin rights on there own desktop or server can just create a local user with the right name and run a C program and get similar access. It is never a good idea to accept an arbitrary asserted ID without some form of validation.
Prior to CHLAUTH which was added in MQ v7.1 the default configuration of a new queue manger was to allow the asserted id to be administrative unless you protected against this, this could be as simple as passing the value "mqm" to a linux queue manager or simply passing a blank value, if the MCAUSER was also blank on the SVRCONN channel the channel would run under the authority of the process running the listener which would be the "mqm" user.
With 7.1 and higher a new queue manager is created by default with CHLAUTH(ENABLED) and a few default CHLAUTH rules, one restricts access to all channels that start with "SYSTEM" and another denies connections to SVRCONN channels from any user MQ determines has administrative authority. Note that if a queue manager was created at a version prior to 7.1, then later upgraded to 7.1 or higher the default is to leave CHLAUTH(DISABLED), so unless you alter it to set it to ENABLED at some point you do not have these protections.
On the Java/JMS client side prior to 7.1, if you did not set the username a blank value would be passed in the RemoteUserIdentifier, after 7.1, if you do not set the username then the client defaults to sending the value of the java system property user.name, if you do set the username it is still sent in favor to the user.name value.
Since MQ v7.0 non-java APIs have had the ability to send an additional structure to the queue manager during the channel connection called the MQCSP which has fields for a username and password and AuthenticationType, but similar to what I said about RemoteUserIdentifier/RemotePassword prior to v8 there was no built in functionality to actually authenticate or do anything with these values, you would need to use a security exit to get this functionality. Note the MQCSP.AuthenticationType field has two possible values, MQCSP_AUTH_NONE (indicating that you do not want authentication performed) or MQCSP_AUTH_USER_ID_AND_PWD (indicate that you want authentication performed).
Since MQ v8.0 the Java and JMS APIs now also have the ability to send the username and password values in the MQCSP instead of the MQCD RemoteUserIdentifier/RemotePassword fields, but for all versions of MQ v8.0, MQ v9.0.0.x (LTS) and 9.0.x (CD), 9.1.0.x (LTS), and 9.1.x (CD) the default is to send the username and password in compatibility mode still using the RemoteUserIdentifier/RemotePassword fields of the MQCD. To have Java and JMS use the MQCSP, you have to specifically set a property. When the specified username and password is sent in the MQCSP the RemoteUserIdentifier is filled in with the java system property user.name by default. Note that in 9.3 it seems the default is now send the username and password in the MQCSP structure instead of in compatibility mode.
At v8.0 and later the queue manager now has the feature called CONNAUTH which can authenticate a username and password that is sent via either MQCSP or RemoteUserIdentifier/RemotePassword. There are various configuration options around how this is configured, but by default at v8 or later if a username and password (note the password could be empty in this case) are sent to the queue manager and MQCSP.AuthenticationType is set to MQCSP_AUTH_USER_ID_AND_PWD, they will be validated to be correct, and if an administrative user is sent to the queue manager it must provide a password and be correct.
For Java/JMS APIs, if no MQCSP is sent by the client but RemoteUserIdentifier and RemotePassword both have a value, the queue manager will build a MQCSP with MQCSP.AuthenticationType set to MQCSP_AUTH_USER_ID_AND_PWD and validate it the same way, note in this case if RemotePassword is empty a MQCSP structure is not built.
Conclusion:
Based on the description of how you state that the Java app with v6 jars is functioning, this means that anyone that has access to connect the MQ listener port of your queue manager can specify any username that has access to MQ including the queue manager admin user. It would be much better to send a username and password that will be validated, or use client TLS certificates to assert identity (When combined with setting the channels SSLPEER and a MCAUSER (or a CHLAUTH SSLPEERMAP rule) you can greatly restrict who can connect to your channel, and you eliminate the ability for them to assert any arbitrary username.

Related

Ibm mq, sending messages to queue without specifying a user

I have a spring app, and I've received information to connect to ibm mq. I've got everything except user and password. When I was using docker image I had a user and password (specified using MQ_APP_PASSWORD env. variable). It's obvious that I can connect to ibmmq queue with just a username, but the question is, if I can connect without both ? does ibmmq allow for something like this ?
The MQ Advanced for Developers container images come with a default configuration which enables security. In this configuration, applications connect using the DEV.APP.SVRCONN channel. I think that you should take a look at the CHCKCLNT attribute in CONNAUTH. In the default configuration, the CHCKCLNT attribute on the channel (CHLAUTH) is set to REQUIRED if you've set a password on the container (by setting the MQ_APP_PASSWORD environment variable), or set to ASQMGR otherwise. The queue manager setting which this will fall back to, is set using CONNAUTH, on the AUTHINFO object, which is set to REQDADM by default. So, not sending a user ID and password should already work. However, if not, try adjusting the AUTHINFO object's CHCKCLNT setting. Note that this could also have a knock-on effect on the DEV.ADMIN.SVRCONN channel, which also uses this default.
You can change the MQ configuration used in a Docker image by adding an MQSC as described here. You will need to refresh security at the end of your MQSC.

Difficulty connecting to Websphere MQ manager using domain authentication

This is a follow-up to: Can create Websphere Queue Manager but not connect
I'm trying to set up MQ on a development machine, but if I try to connect to it using my domain account it's unable to authenticate (AMQ4999). Digging a little further I find this in the error logs:
AMQ8079: Access was denied when attempting to retrieve group membership
information for user 'xxx#domain'.
Now I'm well aware of the known issue with MQ where it fails to authenticate domain accounts since it's unable to access their member information, and have confirmed from the logs that this is definitely what's happening here, so I tried overriding this using the following script gleaned from the previous post:
DEFINE CHL('DOTNET.SVRCONN') CHLTYPE(SVRCONN) MCAUSER('MUSR_MQADMIN#hostname')
SET CHLAUTH('DOTNET.SVRCONN') TYPE(BLOCKUSER) USERLIST('nobody')
SET CHLAUTH('DOTNET.SVRCONN') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) ACTION(ADD)
However, even with this channel in place I still cannot connect to the queue manager while logged into my domain account. I'm still plagued with the exact same error I was getting previously. One thing I did notice was that MQ Explorer reports the channel as inactive even though I started it (although judging by my reading from IBM's website this is normal).
I'm still very new to MQ so I think I'm either missing something or did something wrong, but ideally I would like to be able to set up a dev environment where I can hit the service without having to rely on the 'runas' command. I should also emphasize that this is strictly for dev/learning so obviously I'm not concerned about security.
Update:
I found out what I was doing wrong -- sure enough I was missing a step. A little more background. Upon creating the QM I was trying to connect to it using a simple C# client. Originally I wrote code that looked like this:
var queueManager = new MQQueueManager("MyQueueManager", MQC.MQCNO_STANDARD_BINDING);
Also, when trying to connect via MQExplorer both appears to be using my domain credentials to authenticate. However when I explicitly created a properties object and specified the channel like such:
var props = new Hashtable() {
[MQC.HOST_NAME_PROPERTY] = "localhost",
[MQC.PORT_PROPERTY] = 1414,
[MQC.CHANNEL_PROPERTY] = "DOTNET.SVRCONN",
[MQC.USER_ID_PROPERTY] = "DevMQUser",
[MQC.PASSWORD_PROPERTY] = "p#$$w0rd"
};
var queueManager = new MQQueueManager("MyQueueManager", props);
Then everything worked correctly. I still need to run MQExplorer.exe as a local user (even explicitly setting credentials in Connection Details > Properties doesn't seem to work), but this isn't a big deal.
Thanks for the suggestions.
Try changing...
SET CHLAUTH('DOTNET.SVRCONN') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL)
To...
SET CHLAUTH('DOTNET.SVRCONN') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(MAP) MCAUSER(MUSR_MQADMIN)
The USERSRC(CHANNEL) says to take the ID that is presented to the channel, in this case the local process ID of your logged-in account, to override MCAUSER.
MQ Security diagnostics
For connectivity issues over channels, grab SupportPac MS0P and install into MQ Explorer. Then turn on Authorization Events and Channel Events and recreate the problem. If the connection is blocked by a CHLAUTH record, this shows up in the Channel Event queue. If it is blocked by OAM it shows up in the QMgr Event queue. From Explorer with MS0P installed, right-clicking on the queue name from the Queues panel opens a context dialog that includes "Format event messages" as an option. Select is and MS0P will parse the PCF message into human-readable values that show all the parameters that were presented to MQ and why it blocked the connection.
IBM MQ v8
If this is v8 of MQ, you also have ID and password checking to configure. If the QMgr points to an AUTHINFO record that specifies ID and password checking (IDPWOS) the password can't be blank if the ID is set. Even if the password authentication is set to OPTIONAL the check will be made if an ID is present on the channel, which the client code will ensure is true unless specifically overridden.

WebSphere MQ C API Sending client ID with a client connection

Hi I'm using the CSP struct to send the client ID on a client application, my goal is to get the queue operation being authorized by the OAM based on this ID and not the ID of the process my application is running under. What I observe is:
If my process is running as alice and I send bob in the MQCSP I get a 2035 on the MQCONNX call.
If a I create a user alice on my local machine and execute the program as alice sending alice in the MQSCSP the call succeeds.
If I run the program as alice but send bob in the MQCSP the MQCONNX fails with a 2035 reason code.
Why, apparently, in the 3rd case the ID used for authorization seems to be the ID sent in the MQSCP field while not in the 1st case?
Is there anything that needs to be set in addition to use the fields in the MQSCP structure to send the CLNTID in the options parameter of the MQCONNX call?
The environment is WMQ 7.5 running on Linux.
The code is:
MQCNO mqcno = {MQCNO_DEFAULT};
mqcno.Version = MQCNO_VERSION_5;
// Identification
if (mqpar->user != NULL) {
MQCSP sp = {MQCSP_DEFAULT};
sp.Version = MQCSP_CURRENT_VERSION;
sp.AuthenticationType = MQCSP_AUTH_USER_ID_AND_PWD;
sp.CSPUserIdPtr = mqpar->user;
sp.CSPUserIdLength = strlen(mqpar->user);
sp.CSPPasswordPtr = mqpar->pwd;
sp.CSPPasswordLength = strlen(mqpar->pwd);
mqcno.SecurityParmsPtr = &sp;
}
MQCONNX("",&mqcno,mgrd,retc,reasc);
Thanks for any help.
Adding extra information from subsequent comments made by OP.
The intention of writing this code is not authenticate the connection, but to get the authorisations checks made by the OAM be based on the user sent within the MQCSP and not the user ID that the process on the client side is running under.
I'm trying to achieve the same effect of configuring a Connection Factory in the Java WebSphere Application Server, that if I'm not wrong transmit the userid on a connection to the manager based on the Authentication Alias one specify for the Connection Factory.
What do you have on the queue manager side of things to actually do anything with your MQCSP? Since you are only at V7.5, the queue manager doesn't have it built in (that is in V8), so if you don't have a security exit your MQCSP is being ignored.
Actually 'ignored' is not quite accurate, I think the built-in behaviour checks that the MQCSP user is and the run-as user ID are the same. But nothing will be checking the password unless you have a security exit or use MQ V8.
More information as a result of more from the OP
'C' client works differently from the Java client in this regard. The Java client uses the MQEnvironment set user ID as the run-as user ID. A historical anomaly from a time when Java could not obtain the client side process ID.
I think you have three choice here:-
Use MQ V8 with ADOPTCTX(YES)
Use CHLAUTH rules to set the server-side MCAUSER based on the client IP address and/or run-as user.
Write a small security exit to copy the MQCSP user ID into the MCAUSER

WebSphere MQ MCA user ID

I am using WebSphere MQ version 8.0 to create a remote queue.But it asks for MCA User ID. I am not sure what to enter here. Please help me how to retrieve this.
There is no field on the definition of a QREMOTE object for MCAUSER. Are you perhaps creating a channel definition instead?
If you are creating a channel definition then the MCAUSER field can be used in the following ways:-
Left blank and allowed to adopt the user ID from the client (not recommended unless you can trust your network setup and thus the client machines).
Left blank, and assigned by a CHLAUTH rule (preferred because you can set it dependent on other criteria allowing you to be more convinced a client machine is allowed to connect at all).
Left blank, and then set by a security exit (consider if you are pre-V7.1) and can't use the previous bullet).
Set to a user ID known to the O/S of the queue manager's machine (again not recommended because now all connections to that channel will run with that user ID).
If this doesn't answer, perhaps you can provide a screenshot of exactly what you were doing?

Hermes JMS cannot connect to Websphere MQ 7.1 (2035 error)

I am trying to connect to Websphere MQ 7.1 with Hermes JMS but I am not able to. I have followed their giude, loaded all the jars without problems, set the plugin, set all the variables (hostname, port, transportType, queuemanager), checked the box at the bottom that says user and typed the username and password and after confirming I tried to discover however I get the following message back:
com.ibm.mq.MQException: MQJE001: Completion Code '2', Reason '2035'.
at
com.ibm.mq.MQManagedConnectionJ11.(MQManagedConnectionJ11.java:233)
at
com.ibm.mq.MQClientManagedConnectionFactoryJ11._createManagedConnection(MQClientManagedConnectionFactoryJ11.java:553) at
com.ibm.mq.MQClientManagedConnectionFactoryJ11.createManagedConnection(MQClientManagedConnectionFactoryJ11.java:593)
at
com.ibm.mq.StoredManagedConnection.(StoredManagedConnection.java:95)
at
com.ibm.mq.MQSimpleConnectionManager.allocateConnection(MQSimpleConnectionManager.java:198)
at
com.ibm.mq.MQQueueManagerFactory.obtainBaseMQQueueManager(MQQueueManagerFactory.java:882)
at
com.ibm.mq.MQQueueManagerFactory.procure(MQQueueManagerFactory.java:770)
at
com.ibm.mq.MQQueueManagerFactory.constructQueueManager(MQQueueManagerFactory.java:719)
at
com.ibm.mq.MQQueueManagerFactory.createQueueManager(MQQueueManagerFactory.java:175)
at com.ibm.mq.MQQueueManager.(MQQueueManager.java:647) at
hermes.ext.mq.MQSeriesAdmin.getQueueManager(MQSeriesAdmin.java:107)
at
hermes.ext.mq.MQSeriesAdmin.discoverDestinationConfigs(MQSeriesAdmin.java:280)
at
hermes.impl.HermesAdminAdapter.discoverDestinationConfigs(HermesAdminAdapter.java:82)
at
hermes.impl.DefaultHermesImpl.discoverDestinationConfigs(DefaultHermesImpl.java:1126)
at
hermes.browser.tasks.DiscoverDestinationsTask.invoke(DiscoverDestinationsTask.java:77)
at hermes.browser.tasks.TaskSupport.run(TaskSupport.java:175) at
hermes.browser.tasks.ThreadPool.run(ThreadPool.java:170) at
java.lang.Thread.run(Thread.java:662)
After a few hours of trial and error and research on the net, it seems that the issue is that it cannot connect due to bad authorization however I am able to connect using Java code (Using same lib MQQueueConnectionFactory) and I am also able to connect using QueueZee with the exact same libraries, get a list of all queues and browse them so I know user authorization issues should not be the problem.
I am running Hermes JMS 1.14 and I tried using both Java 1.6.0_33 and 1.7.0_5. Websphere MQ is running on version 7.1.0.0 and the libraries were gotten from this installation on a remote server.
I tried setting the channel variable to SYSTEM.DEF.SVRCONN which is what I used in QueueZee to get it to work but still the same issue.
Has anybody seen this issue before and hopefully can shed some light in the situation?
At V7.1 the new CHLAUTH rules shut off access to all SYSTEM.* channels except SYSTEM.ADMIN.SVRCONN by default and do not allow any administrative access on any SVRCONN channel by default. In order to diagnose this it would be necessary to know what channel was used, the CHLAUTH rules that are set, the channel definition (in particular, the MCAUSER value) and whether the ID used is in the mqm group.
You didn't mention whether the QueueZee setup was also to a V7.1 QMgr or this one in particular. Taking a wild guess, I'd say that CHLAUTH rules are enabled and that the SYSTEM.DEF.SVRCONN channel is disabled at this point. Recommended steps are to define a new channel whose name doesn't start with SYSTEM. and make sure the ID used is not in the mqm group but is authorized as a non-admin ID.
Alternatively, an ID in the mqm group can be used but you'd have to define a CHLAUTH rule to allow it to work. For example, the default CHLAUTH rule uses CHANNEL(*) BLOCKUSER(*MQADMIN) and you could change that to CHANNEL(THE.NEW.CHL.NAME) BLOCKUSER('nobody'). The new rule would be more specific than the old rule and thus take precedence on your channel. It tells the QMgr to block the user ID 'nobody' but omits any mention of *MQADMIN. Since 'nobody' doesn't have access anyway but since *MQADMIN is not mentioned (and thus not blocked by thei rule) the effect of the rule is to allow admins on this channel.
As a quick, dirty and temporary measure, you can also ALTER QMGR CHLAUTH(DISABLED) to get the same behavior as in v7.0 and earlier QMgrs. Be aware though that this allows anonymous remote admin and remote code execution using the mqm user ID. That's why the default settings were changed. Now you must explicitly provision remote admin access if you need it.
For more on this topic, I recommend the Securing Your QMgr presentation from the IMPACT conference.
Note that the password the app sends in is not checked by the QMgr. The field exists so that channel exits can validate the password against AD, LDAP, etc. Without such an exit, the password is ignored. The user ID passed in by the client is either accepted at face value or modified by the channel's MCAUSER or by CHLAUTH rules.
Finally, when having authorization problems the easiest way to diagnose is to ALTER QMGR AUTHOREV(ENABLED) and then use SupportPac MS0P to decode the PCF messages in WMQ Explorer. The auths errors end up in the QMgr Event queue. Each message tells you the object that failed auths, the API call made against that object, the options of the call and the ID that made the call. Often we find the ID making the call isn't the one we wanted or that the program is using options it isn't authorized for so this can be extremely helpful.
Not really an answer, just a little research on the problem.
I have faced the same problem about hour ago. I am passing the username like domain\sortoflongusername and what i see in systemlog on WSMQ server is that my username is being truncated to 12 symbols.
I'm not really familiar with hermesJMS and soapui at all (just wanted to offer it to our testers to check it out as testing platform), so maybe anyone here does know about roots of this problem.

Resources