Using LDAP with MQ multi platform.

MQ multiplatform can use LDAP as a userid and group repository, so you can logon to any machine where MQ is running, and use your corporate userid and password.

I’ve logged on to MQ on my Linux machine, and used my z/OS userid and password. It was pretty easy to set up (I had prior experience of using LDAP) but although it didn’t quite behave as I thought it would – I thought this was pretty clever.

Once you have installed LDAP, started it, and created your directory structure, (user, groups) and access permissions, you can start to use it. I’ve documented some of the initial settup here. It covers some of the concepts referred to below.

I used LDAP (Tivoli Directory Server) on z/OS as my LDAP server.

Contents

I’ve also written using LDAP with MQ and nested groups (MQ NESTGRP).

Using LDAP from MQ Multiplatform

The IBM documentation for this is so-so. It gives examples, but the examples didn’t work for me, but they were enough go point me in the right direction.

Start here.

I created a LDAP.MQSC file with

DEFINE AUTHINFO(MYLDAP) +
AUTHTYPE(IDPWLDAP) +
CONNAME(‘10.1.1.2(389)’) +
AUTHORMD(SEARCHGRP) +
BASEDNG(‘o=Your Company’) +
BASEDNU(‘o=Your Company’) +
LDAPUSER(‘cn=adcda, o=Your Company’) +
LDAPPWD(‘adcdapw1’) +
SECCOMM(NO) +
CLASSUSR(‘ibm-nativeAuthentication’) +
CLASSGRP(‘groupOfNames’) +
GRPFIELD(sn) +
SHORTUSR(sn) +
REPLACE

ALTER QMGR CONNAUTH(MYLDAP)

REFRESH SECURITY TYPE (CONNAUTH)
* ALTER QMGR CONNAUTH(SYSTEM.DEFAULT.AUTHINFO.IDPWOS)

Where the key fields for connecting to LDAP are

  • conname – the IP address of the LDAP server.
  • ldapuser and ldappwd – userid and password to access LDAP.
  • seccomm – use TLS/SSL to contact the LDAP server. I used “no” while setting this up.

the key fields for identifying users are

  • basednu – the subtree to be used for userids, for example all users are one level under ou=user,o=myorg.
  • classusr – is the objectclass attribute to identify the userid. The default is inetOrgPerson.
  • shortusr – the dn identifiers are too long. MQ needs IDs with 12 characters or less. This attribute says which attribute to use.

the key fields for identifying which groups an id belongs to

  • authormd – how to search for the authorisation.
  • basedng – the subtree to be used for groups, for example ou=group,o=myorg.
  • classgrp – the objectType which objects must have to be recognised as a group
  • grpfield – the simple name of the group
  • findgrp – what to filter for. For example ‘member’

Being a careful person, I started an interactive runmqsc session in one terminal, and used runmqsc … < LDAP.MQSC in another window. This way if there were problem I could use the interactive session to reset the QMGR CONNAUTH (as in the comment above). I know that the userid that started the queue manager, does not need a password; so if you issued strmqm qma you can use runmqsc qma without a userid and password. It gets harder if your id is not the id the queue manager is running under.

LDAP definition of my logon userid

The userid I wanted to use with MQ was defined

dn: cn=ibmuser, o=Your Company
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: ibm-nativeAuthentication
objectclass: inetOrgPerson
cn: ibmuser
sn: ibmuser
ou: test
st: cn=group,ou=groups,o=your Company
st: cn=mqadmin,ou=groups,o=your Company
ibm-nativeId: ibmuser

The authinfo data has

BASEDNU(‘o=Your Company‘) +
CLASSUSR(‘ibm-nativeAuthentication‘) +
SHORTUSR(sn) +

When I try to logon with userid ibmuser, MQ issues an LDAP query for the record with

  • sn=ibmuser
  • with an object class = ‘ibm-nativeAuthentication (which provides the RACF support for userid and password)
  • in the subtree o=Your Company

Check the LDAP configuration when things go wrong

It took me a few hours to determine why I could logon with one id, but not another id. Some LDAP entries worked, and some did not. It turned out to be an Access Control List (ACL) set up problem, where the LDAPUSER userid was not authorised to see some of the records. With the above AUTHINFO object, the query that MQ uses to check authorisation is like

ldapsearch -h 127.0.0.1 -D “cn=adcda, o=Your Company” -w ? -b “o=Your Company” “&(objectClass=ibm-nativeAuthentication)(sn=zadcdc)”

Where the parameters match up with the authinfo object above, and zadcdc is the userid trying to logon.

If you get no data back, get an authorised person (cn=ibmuser…) to issue the command for the user problem userid zadcdc:

ldapsearch -h 127.0.0.1 -D “cn=ibmuser, o=Your Company” -w ? -b “o=Your Company” “&(objectClass=*)(sn=zadcdc)”aclentry aclsource

The aclentry will give you the userids or groups who are authorised to use the entry, and the access they have.

The aclsource tells you which node in the tree the ACL was inherited from (for example aclsource=o=Your Company says it came from the root node). I had set up an ACL for my zadcdc which did not include my LDAPUSER.

Setting up MQ connect authorities

You can issue the command

setmqaut -m qml -t qmgr -p ibmuser +connect

to give ibmuser connect authority.

You can use LDAP groups for example

setmqaut -m qml -t qmgr -g “cn=mqadmin,ou=groups,o=your Company” +connect.

How do you set up a group in LDAP?

This is where it gets interesting. You can define a static group with its list of members, or create a dynamic group which is more flexible and “modern” (where modern is within the last 30 years).

Using a static group (with a list of members defined in it)

You can define a static group in LDAP using

dn: cn=mqstatic,ou=groups,o=your Company
objectclass: groupOfNames
ou:groups.
member: cn=ibmuser,o=your Company
member: cn=adcdb,o=your Company

It has two members.

When the userid authenticates, the queue manager asks LDAP for the groups that the userid is in; using the AUTHINFO definitions CLASSGRP(‘groupOfNames’) + GRPFIELD(…) FINDGRP(‘…’) a query is done for the groups which have the userid id. For example with

BASEDNG(‘ou=groups,o=your Company’) +
CLASSGRP(‘groupOfNames’) +
FINDGRP(‘member’) +

and cn=ibmuser o=your Company. The query is

(&(objectClass=groupOfNames)(member=cn=ibmuser, o=Your Company) in subtree (ou=groups,o=your Company)

The ldap search asking to return the member attribute

ldapsearch … -b “ou=groups,o=your Company” “(&(objectClass=groupOfNames) (member=cn=ibmuser, o=Your Company)) ” cn

gave two group names –

cn=mqstatic2,ou=groups,o=your Company
cn=mqstatic,ou=groups,o=your Company

This gives the information as to which groups a user is in. MQ then saves the userid and group information, and does not need to go to LDAP the next time the userid needs access checking.

Knowing which groups a userid is in, MQ can then decide on the access by comparing with the setmqaut definitions.

Using a dynamic group – or using a group attributer in the user record.

Instead of a group having a list of members, you can add information to the user’s record.

For example

dn: cn=adcda, o=Your Company
changetype: add
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: ibm-nativeAuthentication
sn: adcda
group: cn=group,o=your Company
cn: mq
ibm-nativeId: adcda

Unfortunately there is no “group” attribute defined in the LDAP schema, so I had to find another attribute to use. I used st for state. I used st: cn=group,o=your Company instead of group: cn=group,o=your company.

In my MQ AUTHINFO definition I had

BASEDNU(‘o=Your Company’) +
CLASSUSR(‘ibm-nativeAuthentication’) +
SHORTUSR(sn) +
AUTHORMD(SEARCHUSR) +
FINDGRP(st,‘) +
BASEDNG(‘ou=zzzzz,o=your Company’) +

MQ did an LDAP search using this information

ldapsearch… -b “o=Your Company” “&(objectClass=(ibm-nativeAuthentication)
(sn=ibmuser))” st

Which is just a display of the userid information – and return the fields with the attribute you specified(st). It returned

cn=ibmuser, o=Your Company
st=cn=group,ou=groups,o=your Company
st=cn=mqadmin,ou=groups,o=your Company

This tells MQ to use the groups

cn=group,ou=groups,o=your Company and cn=mqadmin,ou=groups,o=your Company.

You can use the setmqaut command to give the group access

setmqaut -m qml -t qmgr -g “cn=mqadmin,ou=groups,o=your Company” +connect

Once this was done, the cn=ibmuser could connect to MQ using groups.

Can I use LDAP to hold my setauth information?

Only the group and userid information are held in LDAP, all the other information is held in the queue manager. You cannot use LDAP to have your setmqaut configuration in LDAP, and shared by multiple queue managers. You still have to use setmqaut to set up each queue manager access.

Giving userids access to MQ objects

You can use the setmqaut command or the set authrec runmqsc command to give principals or groups access to resources.

For example

setmqaut -m qml -n CP0000 -t queue -g “cn=mqstatic,ou=groups,o=your Company” +inq

I’ve changed the definitions in LDAP – when will they get picked up?

Changes get picked up when

  • the queue manager is restarted
  • when the resfresh security, refresh security type(authserv) or the refresh security type(connauth) command is issued.

I’ve started LDAP now what do I do?

Contents

What is LDAP?

I found this a good introduction on LDAP; the structure of the data, searching and filters.

I’ve written up

Setting up LDAP on z/OS

I created a new LDAP instance on z/OS see getting started with LDAP on z/OS, and the definitions and JCL I used to create LDAP. I used the standard schema /usr/lpp/ldap/etc/schema.user.ldif and the IBM extensions /usr/lpp/ldap/etc/schema.IBM.ldif which give you the attributes for working with RACF etc.

Now what do I do?

It is much easier to set up your LDAP structure properly, before you start adding in lots of records, rather than try to change the structure it once you have populated it with all your data. You could be agile, develop your LDAP data, back up the data, and “just” recreate the LDAP repository once you know what you want. Where “just” means write Python or Rexx scripts to take the LDAP data and convert to the new format, for example adding additional information to every definition before adding it to the dictionary.

Because I did not fully understand how Access Control Lists work, I managed to make some of my data invisible to the end user requests, so it is easy to make mistakes when you do not know what you are doing. This blog should give you some hints about setting up your LDAP environment, and avoid some of the rework.

Background to LDAP

LDAP is a generalised directory with an application interface over IP.

The data is held in a hierarchical(upside down tree) form, for example the top of the tree may be called o=myorg. Where o stands for Organisation.

You configure this top of the tree in the LDAP config file for example

# this defines a file based database
database LDBM GLDBLD31/GLDBLD64
# this says it can use RACF for password checking
useNativeAuth all
#this is the top of the tree
suffix “o=myorg
# this is the location on disk of the database
databaseDirectory /var/ldap/ldbm

The next levels down might be

  1. ou=users, o=myorg
  2. ou=groups,o=myorg
  3. ou=corporate data,o=myorg

The data for the first subtree could be stored in DB2, the data for the second subtree could be in files, and the data for the third subtree could be in another LDAP, somewhere else.

A “record” or leaf of the tree could be identified by

dn:cn=colin paice,c=GB,ou=users,o=myorg

Where

  • cn= is the common name
  • c= is the country name
  • ou= is the organisational unit
  • o= your organisation.

With each record you need one or more objectTypes. Object types have attributes. For example an objectType of person can have an attribute telephoneNumber. If you want to use telephoneNumber you need an objectType that supports it.

A typical entry might be

dn: cn=mq, o=Your Company
objectclass: top
objectclass: organizationalPerson
cn: mqadmin
telephoneNumber: 1234567
telephoneNumber: 987654321
sn: mqadmin

Logging on with z/OS userid and password.

I set up LDAP so I could logon to the Linux queue manager, and use my z/OS userid and password. For this I had

dn: cn=ibmuser, o=Your Company
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: ibm-nativeAuthentication
cn: ibmuser
sn: snibmuser
ibm-nativeId: IBMUSER

Where

  • objectclass: ibm-nativeAuthentication is for the RACF authorisation
  • ibm-nativeId: IBMUSER says use the RACF userid IBMUSER when cn=ibmuser, o=Your Company is used.

I also set up an Access Control List entry for this userid, so it can search, and read entries

dn: o=Your Company
changetype: modify
aclEntry : access-id:cn=ibmuser, o=Your Company:normal:grant:rscw

This says

  • for the subtree under the distinguished name of o=Your Company. DNs of ou=groups,o=myorg, and ou=users,o=myorg would be more typical subtree names.
  • the dn cn=ibmuser, o=Your Company, the dn of the user for this ACL. This would normally be a group rather than a userid. You can have multiple entries for each dn.
  • has read, search and compare and write on normal fields. A social security number is “sensitive” field, and a password is a “critical” field. This ACL only gives access to normal fields.

What do you want in a year’s time?

It is much easier to set up your LDAP structure properly before you start, rather than try to change the structure it once you are using it.

For example you could have a flat tree with entries like

  • dn:cn=colin paice,o=myorg for users
  • dn:cn=mqadmin,o=myorg for groups

This will quickly become hard to manage. You may find the following are better.

  • dn:cn=colin paice,ou=users,o=myorg for users
  • dn:cn=mqadmin,ou=groups,o=myorg for groups
  • dn:cn=testmqadmin,ou=groups,o=myorg for a different group with the department name in it.

or

  • dn:cn=colin paice,c=gb,ou=users,o=myorg for users by country
  • dn:cn=mqadmin,ou=test,ou=groups,o=myorg for test groups

Administration

You can give access to administer nodes in the tree, at the subtree level. For example for the sub node in the tree ou=test,ou=groups,o=myorg might have administrators.

  • cn=hqadmin,ou=groups,o=myorg
  • cn=colin paice,c=gb,ou=users,o=myorg

Having a structure like dn:cn=mqadmin,ou=test,ou=groups,o=myorg means you can give the test manager admin control to this test groups, but the test manager has no authority over the production groups. If you had a structure like dn:cn=mqadmin,ou=groups,o=myorg. It makes it much harder to separate the responsibilities.

For the top of the tree o=myorg, you could set up only the following group has administration.

  • cn=hqadmin,ou=groups,o=myorg

From a performance perspective it will be cheaper and faster to access data in a subtree, rather than search the whole tree – bearing in mind you could have millions of entries in the tree.

Note: The adminDN userid in the LDAP config file has authority over every thing. The ACLs on the tree, or subtree, or record define who has administration authority.

Controlling access

You control access using Access Control Lists. An ACL looks like

dn: ou=users,o=myorg
changetype: modify
replace: aclEntry
aclEntry : access-id:cn=ibmuser, o=Your Company:
  object:ad:normal:grant:rscw:sensitive:rscw:
  critical:rscw:restricted:rscw
aclEntry: group:cn=authenticated:normal:rsc

This defines the access for the subtree ou=users,o=myorg

  • cn=ibmuser, o=Your Company can add delete entries under the subtree; and use any of the fields including sensitive ( eg social security number) and critical (eg password).
  • group: cn=authenticated any user who has authenticated can read, search and compare on normal fields. They cannot see or select on sensitive or critical fields.
  • object:a|d is to add or delete objects in the subtree.
  • normal:… sensitive… critical… these give access to the fields in the data ( so you can issue an ldap_search for example). You can specify <grant:|deny:> attributes, and so give access or remove access
  • restricted: To update ACLs you must have read and write access.
  • You can specify what access people have to individual fields, so you can give them access to most fields, but deny access to specific fields.

The userid defined in the LDAP configuration file under adminDN can change anything. When I messed up my data, I changed the config file to use adminDN cn=admin and password secret1, stopped and restarted the LDAP server, and fixed my problem. I undid the changes to the config file, stopped and restarted the server, to get back to normal operation.

You need to plan on what access you want, at which levels of the tree, and who has what access to which fields.

If is better to give access to groups, and add users to groups, than to give access to userids. For example, if you have many ACLs, if someone joins the team, changing the group to add the id is one change. If you have to change each ACL, and add the id, you may have many changes.

What is going to use it?

Programs using LDAP may have requirements on the data structure. For example MQ can use userid and group information in LDAP. It expects the data to be under a single subtree. You could specify groups are to be found under, ou=test,ou=groups,o=myorg. You cannot say under ou=test,ou=groups,o=myorg and ou=production,ou=groups,o=myorg. Similary, users would be under ou=users,o=myorg. If you specified the subtree c=gb,ou=users,o=myorg, then this limits users to having a GB userid, which may not be what you want.

Using groups

Although LDAP on z/OS can support static groups (with a list of members), nested groups containing groups, and dynamic groups ( where you can say those users with ou=production), the group of groups and dynamic groups cannot be used to check group membership.

You can query and display all users in all group types.

You can say give me the only the group names, where colinPaice is a member; when the group is a static group, not groups of groups, nor dynamic groups. This makes managing groups much harder, and you may need to have bigger groups than specific smaller groups.

You cannot exploit the flexibility of the nested and dynamics groups.

What fields do you want in the records.

If you have a definition like

dn: cn=ibmuser, o=Your Company
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: ibm-nativeAuthentication
cn: ibmuser
sn: snibmuser
ibm-nativeId: IBMUSER

If you want to use the ibm-nativeId field to give the RACF userid to use, then you need objectclass: ibm-nativeAuthentication.

If you have ibm-nativeAuthentication you must have ibm-nativeId, and may have other fields.

The “must” and “may” fields are defined in a schema.

The schema /usr/lpp/ldap/etc/schema.IBM.ldif has

objectclasses: (
  NAME 'ibm-nativeAuthentication'
  DESC 'Indicates native security manager should be used during authentication.'
  SUP top
  AUXILIARY
  MUST ( ibm-nativeId )
)

The objectClass: person has

objectclasses: (
  NAME 'person'
  DESC 'Defines entries that generically represent people.'
  MUST ( cn $ sn )
  MAY ( userPassword $ telephoneNumber $ seeAlso $ description )
)

This means you must provide a cn and an sn entry, and you can provide other entries

The schema can give information about an attribute

attributetypes: (
  NAME ( 'sn' 'surName' )
  DESC 'This is the X.500 surname attribute, which contains the family name of a person.'
  EQUALITY caseIgnoreMatch
  ORDERING caseIgnoreOrderingMatch
  SUBSTR caseIgnoreSubstringsMatch
)

This shows that for ordering or comparing, it ignores the case of the data, so “colin paice” is the same as “COLIN PAICE”.

You need to decides what fields you want, and how you want the fields to be processed, for example case, and comparison.

You can write your own schema for fields that are unique to your organisation.

What you use the field for is up to you. For example “sn” could be surname, or”short name”. You just have to be consistent and document it.

What tools are there to help me?

You can use the tools provided with LDAP to administer LDAP. For example the ldap_modify command can be used to process a batch of definitions; whole records, or attributes within records.

Eclipse has a plugin Apache Directory Studio. It works well, and looks like it is highly recommended. This plugin allows you to browse and manage entries. I could not get it to display the schema.

How do I backup/export the data

You can use the ds2ldif command. It creates a file in ldif format which can be used to add back all the records (using the ldap_add or ldap_modify commands).

LDAP error messages and codes

This blog post is a repository for the LDAP error codes I experienced, and the actions I took to resolve the problems.

LDAP return codes

Messages include return codes like “3”, but the LDAP programming book has terms like “LDAP_PARAM_ERROR”.

These are defined in

/usr/include/ldap*.h,

SSL initialization failures reason codes.

https://www.ibm.com/docs/en/zos/2.5.0?topic=utilities-ssltls-information-ldap-client#idg18488__failrc

GLD1342E Unwilling to open file or directory ‘/var/ldap/schema’:

File or directory UID 0, UID of program 990023, GID of file or directory 1, GIDs of program (990018).

Colin’s comments

The LDAP started task expects to be the file owner of the /var/ldap/* files. On ADCD they were OMVSKERN:OMVSGRP. I used

chown -R gldsrv:gldgrp /var/ldap/*

to change the file owner.

Object class violation: additional info: R001026 No structural object class specified for ‘cn=ibmuser, o=Your Company’.

Colin’s comments

I had an ldif file with

dn: cn=mq, o=Your Company
changetype: add
objectclass: top
#objectclass: person
#objectclass: organizationalPerson
objectclass: ibm-nativeAuthentication
cn: mq
telephoneNumber: 1234567
telephoneNumber: 12345672
sn: Administrator

And no proper object class. When I uncommented person or organizationalPerson it worked.

R001030 Entry contains attribute ‘ibm-nativeid’ which is not allowed for object class

I was trying to add ‘ibm-nativeid’ to an entry. This attributed belongs to object class ibm-nativeAuthentication. The object has to have this object class, for example, add the lines in the bold font.

dn: cn=colin, o=Your Company
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: ibm-nativeAuthentication
cn: LDAP Administrator
sn: Administrator
ibm-nativeId: COLIN

Credentials are not valid: R004062 Credentials are not valid

By accident I overwrote my administration userid definition.

Colin’s comments

Edit GLD.CNFOUT(DSCONFIG) ( or what every config file you are using)

Comment out the adminDN, and add in the cn=admin and its password.

From

adminDN “cn=ibmuser, o=Your Company”
# adminDN “cn=Admin”
# adminPW secret

to

# adminDN “cn=ibmuser, o=Your Company”
adminDN “cn=Admin”
adminPW secret

  • Stop and restart LDAP.
  • Fix the userid
  • Change the admin definitions back
  • restart LDAP.

R003070 Access denied because user does not have ‘write’ permission for all modified attributes

I used a command like ldapmodify -a -h 127.0.0.1 -p 389 -D “cn=adcda, o=Your Company” -w adcdapw1 -f mqacl.* to change some definitions, but the userid cn=adcda,o=Your Company did not have the correct permissions.

You can enable the LDAP acl trace using f gldsrv,debug 128, and reset it using f gldsrv,debug 0

To change/add/delete an ACL the id needs restricted:rscw

For example

dn: o=Your Company
changetype: modify
replace: aclEntry
aclEntry : access-id:cn=ibmuser, o=Your Company:
object:ad:normal:grant:rscw:sensitive:rscw:critical:rscw
aclEntry : access-id:cn=adcda, o=Your Company:
object:ad:normal:rscw:sensitive:rscw:critical:rscw:restricted:rscw

Insufficient access: R003057 Access denied because user does not have ‘add’ permission for the parent entry

Trying to add an entry.

The userid is not authorised to add an entry. It needs an acl with object:ad ( a for add, d for delete)

dn: o=Your Company
changetype: modify
replace: aclEntry
aclEntry : access-id:cn=ibmuser, o=Your Company:
object:ad:normal:rscw:sensitive:rscw:critical:rscw
aclEntry : access-id:cn=adcda, o=Your Company:
object:ad:normal:rscw

GLD1116E Unable to initialize an SSL connection with …: 515 – Key share list is not valid.

My GSK_CLIENT_TLS_KEY_SHARES GSK_SERVER_TLS_KEY_SHARES environment variables had an invalid value. They had 0021 which is not supported in TLS 1.3.

Look in the gsktrace

GLD1063E Unable to initialize the SSL environment: 416 – Permission denied.

GLD1160E Unable to initialize the LDAP client SSL support: Error 113, Reason -17.

ICH408I USER(GLDSRV ) GROUP(GLDGRP ) IRR.DIGTCERT.LISTRING CL(FACILITY) INSUFFICIENT ACCESS AUTHORITY ACCESS INTENT(READ ) ACCESS ALLOWED(NONE )

In the LDAP config file I had sslKeyRingFile START1.MQRING. The userid GLDSRV did not have read access to the list ring facitity IRR.DIGTCERT.LISTRING CL(FACILITY)

permit IRR.DIGTCERT.LISTRING CL(FACILITY) id(GLDSRV)
SETROPTS RACLIST(FACILITY) REFRESH

GLD1160E Unable to initialize the LDAP client SSL support: Error 113, Reason 705

I had GSK_OCSP_CLIENT_CACHE_SIZE=10000, when I set it to 100, it worked.

GLD1160E Unable to initialize the LDAP client SSL support: Error 113, Reason 2.
GLD1063E Unable to initialize the SSL environment: 202 – Error detected while opening the certificate database.

  • reason code 2: Keyring open error
  • SSL return code 202: Keyring open error

Actions

  • Check value specified
  • Check access
    • rdefine rdatalib START1.MQRING.LST UACC(NONE)
    • SETROPTS RACLIST(RDATALIB) REFRESH
    • permit START1.MQRING.LST class(RDATALIB) ACCESS(READ) id(GLDSRV)

Check the keyring exists ( list the contents of it)

RACDCERT LISTRING(name) ID(COLIN)

Get out a gsk trace .

  • Add GSK_TRACE=0xff to the env file.
  • By default the output goes to gskssl.*.trc
  • Format it using gsktrace gskssl.*.trc gsktrace.out
  • oedit gsktrace.out search for ERROR. I had ERROR gsk_open_keyring(): IRRSDL00 GetData failed: SAF 8, RC 8, Reason 84.
  • These are documented here. 8 8 84 means keyring not found.

GLD1160E Unable to initialize the LDAP client SSL support: Error 113, Reason -99.

Reason -99 is GSK_ERROR_UNKNOWN_ERROR!

I got this when trying to use OCSP with LDAP. I had

GSK_OCSP_RESPONSE_SIGALG_PAIRS=0601050305010804

If I remove the 0804 ( or 0806 or 0805) then startup got past this message.

GLD1116E Unable to initialize an SSL connection 8 – Certificate validation error.

Colin’s comments

I got this many times for many different reasons.

I had Certificate Revocation List processing enabled.

In the GSK trace I had

ERROR check_crl_issuer_extensions(): crlSign bit is not set in KeyUsage
ERROR check_revoked(): Unable to verify CRL issuer extensions: Error 0x03353026

03353026 Incorrect key usage.

Explanation: The key usage certificate extension does not permit the requested key operation.

My CA was not defined properly I needed

keyUsage = keyCertSign, digitalSignature,cRLSign

GLD1116E Unable to initialize an SSL connection openssl SSL alert number 42

Colin’s comments

The certificate sent from the client was missing the authorityKeyIdentifier extension, because the CA certificate is missing.

In the -config xxx.cnf and the specified -extensions … or the default extensions

subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always

You need to change the CA, regenerate the end user certificate, and redistribute the CA.

533 – Remote partner indicates unsupported certificate.

GLD1116E Unable to initialize an SSL connection 402 – No SSL cipher

Colin’s comments 1

I could see from the gsktrace on zOS there was a message ERROR read_client_hello_cipher_select(): No intersection with client cipher suites.

This means the list of available cipher specs on the server did not include the one sent from the client.

Colin’s comments 2

The server’s certificate was not compatible with the list in GSK_V3_CIPHER_SPECS_EXPANDED. For example the list had only EC certificates, but the server was RSA.

Colin’s comments 3

I had a server certificate defined as

SIZE(521) NISTECC …

In the trace I had

EXIT gsk_get_ec_parameters_info(): <— Exit status 0x00000000 (0) EC curve type 34, key size 521
ERROR send_v3_alert(): Sent SSL V3 alert 40 to 10.1.0.2[38736]

INFO edit_ciphers(): Server certificate ec curve 0034 not in supported ecurve tls extension. EC cipher suites disabled

When I changed the size to 256 it worked, and used (C02C,C02B,C024,C023)

From here 0034 is TLS_DH_anon_WITH_AES_128_CBC_SHA

GLD1116E Unable to initialize an SSL connection with 10.1.0.2: 412 –
SSL protocol or certificate type is not supported.

Colin’s comments

The server had been configured for only GSK_PROTOCOL_TLSV1_3=on.

The GSKTRACE output has Client does not support TLS V1.3. No protocol version match found.

GLD1116E Unable to initialize an SSL connection with … 434 – Certificate key is not compatible with cipher suite.

Colin’s comments

The server’s certificate is not consistent with the certificate sent from the client.

For example the server is using an RSA certificate, but the client is sending and EC certificate.

For example server is an RSA certificate. From the list GSK_V3_CIPHER_SPECS_EXPANDED = C024C006C007C008c024c023c025130313011302009E

it chooses 009E. 009E is TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 =

128-bit AES in Galois Counter Mode encryption with 128-bit AEAD authentication and ephemeral Diffie-Hellman key exchange signed with an RSA certificate.

When my elliptic certificate from the client comes in,

Signature Algorithm: SHA256withRSA, Key: Sun EC public key, 521 bits, parameters: secp521r1 NIST P-521

This is incompatible.

When the server certificate is an Elliptic certificate, then certificate C024 is used. C024 is

TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 256-bit AES encryption with SHA-384 message authentication and ephemeral ECDH key exchange signed with an ECDSA certificate.

This works for both of them.

GLD1116E Unable to initialize an SSL connection 440 – Incorrect key usage.

Colin’s comments.

The server’s certificate was defined with KEYUSAGE(CERTSIGN,KEYAGREE)

When I added HANDSHAKE, recreated the certificate, and restarted the LDAP server it worked. KEYUSAGE(HANDSHAKE,CERTSIGN,KEYAGREE).

Certsign says this is a used as a CA.

GLD1116E Unable to initialize an SSL connection with 127.0.0.1: 533 – Remote partner indicates unsupported certificate.

Colin’s comments.

I got this when the GSK_TLS_SIG_ALG_PAIRS=”0403″ did match up with the server’s version.

In the gsktrace for the server I got TLS 1.3 alert 43 received from

In the client’s gsktrace it had

Certificate key algorithm 13, Signature algorithm 25
INFO read_tls13_certificate(): Using client’s signature algorithm list to check server certificate chain
ERROR read_tls13_certificate(): Signature algorithm 25 in server certificate not in client signature algorithms list
ERROR send_tls13_alert(): Sent TLS 1.3 alert 43 to …

in gskcms.h

  • x509_alg_ecPublicKey = 13,
  • x509_alg_sha256WithRsaEncryption = 25,

GLD1116E Unable to initialize an SSL connection with 127.0.0.1: 467 – Signature algorithm not in signature algorithm pairs list.

See previous for 553. The GSK_TLS_SIG_ALG_PAIRS from the client does not mach the server certitificate’s signature.

RACDCERT LIST(LABEL(‘SERVEREC’ )) id(start1)

gives

Signing Algorithm: sha256RSA

This table says 0401 SHA-256 with RSA, so this value is needed in the GSK_TLS_SIG_ALG_PAIRS.

GLD1116E Unable to initialize an SSL connection with …: 516 – No key share groups in common with parter

The configuration was

  • server GSK_SERVER_TLS_KEY_SHARES=0030
  • client GSK_CLIENT_TLS_KEY_SHARES=0024

Specify values with a common value.

TLS 1.3 supports 00300029002500240023

TLS 1.2 supports 00250024002300210019

So you could specify =002300240025

LDAPSEARCH client on Linux

ldap_search_ext: Bad search filter (-7)

with -b “o=Your Company” “&(objectClass=*)”

remove the &()s

-b “o=Your Company” “objectClass=*”

worked

ldap_sasl_interactive_bind_s: Unknown authentication method (-6)
additional info: SASL(-4): no mechanism available:

No certificate was sent from the client to the host.

ldap_sasl_interactive_bind_s: Can’t contact LDAP server (-1)
additional info: A TLS fatal alert has been received.

Colin’s comments 1

The list of certificate types the client sent up ( first part of the handshake) did not match any of the list of supported certificates in GSK_V3_CIPHER_SPECS_EXPANDED=009E002FC027c02dc023c025130313011302

I used Wireshark to display network traffic, and the the list of supported certificate types sent in the client hello.

z/OS gsktrace shows

Initial SSL V3 4-character cipher specs:
009E002FC027C02DC023C025130313011302
SSL V3 cipher C02D skipped due to key algorithm
SSL V3 cipher C023 skipped due to key algorithm
SSL V3 cipher C025 skipped due to key algorithm
SSL V3 cipher 1303 skipped for TLS V1.2 sessions
SSL V3 cipher 1301 skipped for TLS V1.2 sessions
SSL V3 cipher 1302 skipped for TLS V1.2 sessions
SSL V3 cipher specs: 009E002FC027
Using TLSV1.2 protocol
Using V3 cipher specification 009E

  • 009E is TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
  • 002F is TLS_RSA_WITH_AES_128_CBC_SHA
  • C027 is TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256

and the z/OS default key had

Signing Algorithm: sha256RSA
Key Usage: HANDSHAKE, DATAENCRYPT, DOCSIGN
Key Type: RSA
Key Size: 4096

Colins’s comments 2

In the gsktrace I had

ERROR check_ocsp_signer_extensions(): extended keyUsage does not allow OCSP Signing

This is because the certificate used in the ocsp server, did not have

Extended Key Usage: critical, OCSP Signing

Resign the certicate and check the attribute has been set by

openssl x509 -in ocspcert.pem -text -nooutless

ldap_sasl_interactive_bind_s: Can’t contact LDAP server (-1)
additional info: An unknown public key algorithm was encountered.

Colin’s comments

As part of the “certificate verify”, the Signature Algorithm passed to the server, was not in the GSK_TLS_SIG_ALG_PAIRS list in the z/OS LDAP environment file.

Check all relevant are specified

GSK_TLS_SIG_ALG_PAIRS=060105010401030108060805080405030403

ldap_sasl_interactive_bind_s: Can’t contact LDAP server (-1)
additional info: (unknown error code)

I got this when using OCSP for certificate validation. OCSP sent down a flow from the server, and the ldapserver code was not expecting it, so ends.

Action: set

GSK_SERVER_OCSP_STAPLING=OFF

ldap_sasl_interactive_bind_s: Invalid credentials (49)


additional info: R004062 Credentials are not valid (srv_ssl_get_client_info:928)

Colin’s comments 1.

The TLS handshake was accepted, but the mapping of the DN to a userid did not return a userid.

Turn on LDAP trace using f GLDSRV,debug LDAPBE gave

LDAPBE srv_process_bind_request()374: do_bind msgID=1, connID=4, flags=0x22, controls=0x0, DN=”, authType=3, bindType=1, version=3
LDAPBE srv_process_bind_request()939: do_return_bind msgID=1, connID=4, bindDN=”, safUserID=”, dnList=0x0, grpList=0x0, rc=49: R004062 Credentials are not valid (srv_ssl_get_client_info:928)

ERROR srv_process_bind_request()957: Request failed OP code=0 bind=CN=secp521r,O=cpwebuser,C=GB

Map the certificate to a userid – note the ‘.’ in the SDNFILTER name.

//S1 EXEC PGM=IKJEFT01,REGION=0M
//STEPLIB DD DISP=SHR,DSN=SYS1.MIGLIB
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
*RACDCERT LISTMAP ID(ADCDE)
*RACDCERT DELMAP(LABEL('LINUXECEC' )) ID(ADCDE)
*SETROPTS RACLIST(DIGTNMAP, DIGTCRIT) REFRESH
RACDCERT MAP ID(ADCDE ) -
   SDNFILTER('CN=secp521r.O=cpwebuser.C=GB') -
   WITHLABEL('LINUXECEC)
RACDCERT LISTMAP ID(ADCDE)
SETROPTS RACLIST(DIGTNMAP, DIGTCRIT) REFRESH
/*

Once you have defined the mapping, you do not need to restart LDAP, it is picked up on the next usage.

Colin’s comments 2.

The mapping of certificate to userid exists, but the userid is revoked, or otherwise not available.

ERROR srv_ssl_get_client_info() 902: safRc=8 racfRc=8 racfRsn=40

This is from R_usermap (IRRSIM00): Map application user

  • racfrsn 28 – Certificate is not valid.
  • racfrsn 40 – The Distinguished Name length is not valid, or the Distinguished Name string is all blanks (x’20’), all nulls (x’00’), or a combination of blanks and nulls.
  • racfrsn 48 – There is no distributed identity filter mapping the supplied distributed identity to a RACF user ID, or The IDIDMAP RACF general resource class is not active or not RACLISTed.

ldap_ssl_client_init failed! rc == 113, failureReasonCode == 2

This is not listed in table 7 of the LDAP client programming

I turned on trace using ldapsearch .. -d all …

and got

ERROR ldap_ssl_client_init()710: Unable to initialize SSL environment: Error 202
TRACE ldap_ssl_client_init()744: <= Status 113, Reason 2

Error code 202 is in the table = Keyring open error.

ldap_connect()409: Unable to initialize SSL connection to 127.0.0.1[1389]: Error 116, Reason -99

Colin’s comments

This was due to a mismatch in the GSK_TLS_SIG_ALG_PAIRS statement.

ldap_connect()409: Unable to initialize SSL connection to 127.0.0.1[1389]: Error 116, Reason -13

Colin’s comments

This was due to a mismatch in the supported versions of TLS.

ldap_connect()409: Unable to initialize SSL connection to 127.0.0.1[1389]: Error 116, Reason 438

ldap_ssl_socket_initUnable to initialize SSL connection: Error 456.

Colin’s comments

On the system log I had

ICH408I USER(COLIN ) GROUP(SYS1 ) NAME(COLIN PAICE )
CSFOWH CL(CSFSERV )
INSUFFICIENT ACCESS AUTHORITY
ACCESS INTENT(READ ) ACCESS ALLOWED(NONE )

I used the following to get access, and it worked.

permit CSFOWH class(CSFSERV) ACCESS(read) id(COLIN)
SETROPTS RACLIST(CSFSERV) REFRESH

Using groups for authority and access checking is so last century.

I’ve been exploring LDAP as a userid repository (as can be used by MQ multi platform). This got me into an interesting rabbit warren of Role Based Access Control (RBAC) and Attribute Based Access Control(ABAC), and how you set up your repository to hold userid and access information.

In LDAP on z/OS I can set up a user

dn: cn=adcda, o=Your Company
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: ibm-nativeAuthentication
sn:adcda
cn: mqadmin
ibm-nativeId: adcda

With this, if I try to logon with cn=adcda, o=Your Company . It will try to use RACF to check the password I specified and the userid in ibm-nativeId (adcda) is valid. I’ve logged on to MQ on Linux using this definition, and had RACF on z/OS check my password. (I thought this was pretty neat).

This definition has an attribute sn ( surName) of adcda and a cn (commonName) of mqadmin.

LDAP groups.

You can set up statics LDAP groups

These have a list of members

dn: cn=ldap_team_static,o=myorg
objectclass: groupOfNames
cn: ldap_team_static
member: cn=colin,o=myorg
member: cn=colin2,o=myorg

Groups within groups

A group can have a group name to be included

dn: cn=ldap_team_nested,o=myorg
objectclass: container
objectclass: ibm-nestedGroup
cn: ldap_team_nested
ibm-memberGroup: cn=ldap_team_static,o=myorg
ibm-memberGroup: cn=mq_team,o=myorg

You can display group members

ldapsearch …–b “ldap_team_nested,o=myorg” “objectclass=*” ibmallMembers

You can also have smart, dynamic groups

This is the “new” way of doing it – which has been available for about 30 years.

dn: cn=dynamic_team,o=Your Company
objectclass: groupOfUrls
cn: dynamic_team
memberurl: ldap:///o=Your Company?sub?(cn=mqadmin)

This says

  • query the tree under o=Your Company ( a more realistic subtree would be ou=users,o=Your Company)
  • sub, says all levels in the tree (base is search just the specified URL, one is search just one level below the specified URL)
  • list all those with the specified attribute cn = mqadmin.

Instead of updating a group – you add information into the user’s entry, and it would get picked up automatically.

Ideally there would be an LDAP attribute “role” which you could use for this. The default schemas do not have this.

RACF

If you are using RACF you can set up a userid, and connect it to a group. RACF does not support nested groups for authority and access checking.

Access to resources

Using group or Access Control List

Many systems provides group or Access Control List (ACL) to control access to a resource.

For example you might say users in group MQADMIN can update dataset MQ.JCL, and userids in group MQOTHER can read the MQ.JCL dataset.

This has limitations in that the resources are treated individually, so if you have 10 files, you have to grant a group access to 10 profiles.

Role Based Access Control(RBAC)

I struggled initially to see the difference between RBAC and group or ACLs.

With RBAC you do not give update or read access to a resource, you give access to a “task” or “role” like “Maintain records”, “client admin”, or “clerk”. You then give the tasks the appropriate access. You could implement this at a basic level using groups called MAINTREC, and CLIENTADM to give it update access to the resource, and group CLERK with a read access.

Attribute Based Access Control(ABAC)

ABAC seems to take this further. There are products you buy which can do this for you, but I could not see how it was configured or how it worked. Below is my interpretation of how I might configure it using LDAP.

You could have a user defined like

dn: cn=colin paice, o=Your Company
role: doctor
sn:adcda
cn: mqadmin
ibm-nativeId: adcda

A set of resources like

dn: cn=doctor update,o=Your Company
table: HOSPITAL.PATIENT
table:HOSPITAL.XRAYS.DATA
MQQueue:Surgery.queue

This is a list of resources a doctor needs to do their job.

And a set of rules

dn:cn=doctorsRules,o=Your Company
role: doctor
resource: cn=doctor update,o=Your Company
site:London
site:Glasgow
reason:Patient Update

If someone (Doctor Colin Paice) wants up update a patients record, you can do a query using dynamic groups

  1. What roles does Colin Paice have?
  2. What is the resource group for the DB2 table HOSPITAL
  3. Is there a valid rule for the list of roles for Colin Paice, with the resource group for the table HOSPITAL.PATIENTS, where access is from LONDON, and reason is Patient Update.

Is it that simple?

You have to be able to handle the case when a doctor may only look at the patients notes if the doctor is the “attending physician” – so the person is a patient of the doctor. This might mean a “patientOf: Doctors_name” field in the user’s record.

It looks like you have to be very careful in setting this environment up, as you could have many thousands of rules, and it could be very hard to manage.

Even if it is hard, I think the idea of virtual groups, were you select records based on a criteria is a good idea. It may be faster than using groups because it can exploit the index capability of the underlying database, rather than build lists of group membership.

One minute MVS: LDAP defining resources

Having set up an LDAP server, you need to add information to the directory. This is not very well described in the TDS documentation.

Basic overview of data

To add information about a user use a file in USS like colin.ldif

dn: cn=colin, o=Your Company
objectclass: top
objectclass: person
objectclass: organizationalPerson
cn: LDAP Administrator
sn: Administrator

Where

  • the “key” to identify an entry is the dn ..
  • objectclass is the sort of object, and what attributes it can have. It can have many object classes
  • cn: and sn: are attribute values
  • There is a blank line following to indicate end of definition. You can have many of these in a file, to allow you to do a bulk update.

How do I display the contents?

You need to issue a query. This comes in two parts, identifying the user, and the request.

To identify requestor you need something like

ldapsearch -h 127.0.0.1 -D “cn=ibmuser, o=Your Company” -w ? …

and the query for example, to list all the information about cn=ibmuser, o=Your Company add the following to the ldapsearch request above

-b “cn=ibmuser, o=Your Company” “(objectclass=*)”

This gives

cn=ibmuser, o=Your Company
objectclass=top
objectclass=person
objectclass=organizationalPerson
objectclass=ibm-nativeAuthentication
cn=ibmuser
sn=Administrator
ibm-nativeid=IBMUSER

For all information under o=Your Company

-b “o=Your Company” “(objectclass=*)”

For only the list of sn for all users

-b “o=Your Company” “(objectclass=*)” sn

This gives

o=Your Company

cn=colinw, o=Your Company
sn=Administrator

cn=colin, o=Your Company
sn=Administrator

cn=LDAP Administrator, o=Your Company
sn=Administrator

cn=ibmuser, o=Your Company
sn=Administrator

What authority do I need?

Typically you need to be an LDAP administrator, or have the appropriate access to Access Control lists. See here for managing ACLs.

How do I add information?

If I want to add a userid definition for ibmuser (above) so I can login with RACF, I need to add attribute

ibm-nativeId: COLIN

This attribute is in object type

objectclass: ibm-nativeAuthentication

So to be able to specify the ibm-native-ID: attribute, you need to tell specify the object class as well.

My definition is now

dn: cn=colin, o=Your Company
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: ibm-nativeAuthentication
cn: LDAP Administrator
sn: Administrator
ibm-nativeId: COLIN

Add it to the directory

You can add this to the directory using

ldapmodify -a -h … -p … -D “…” -w … -f colin.ldif

Where

  • -a says add (instead of modify)
  • -f colin.ldif is the name of the file with the statements in it.

Modifying an entry

If you want to modify an existing entry, you can change the whole entry, or parts of it.

To add an entry

dn: cn=colin, o=Your Company
changetype: add
objectclass: top

To delete a whole entry

dn: cn=colin, o=Your Company
changetype: delete

To add an attribute to an entry

dn: cn=colin, o=Your Company
changetype: modify
add: attrccp
attrccp: value1
attrccp: value2…

This adds two attrccp values to the definition

To modify an existing attribute

dn: cn=colin, o=Your Company
changetype: modify
modify: ibm-nativeId
ibm-nativeId: PAICE

To delete an attribute

dn: cn=colin, o=Your Company
changetype: delete
delete: ibm-nativeId

This deletes all ibm-nativeID attributes.

If you want to delete a specific attribute specify it after the delete: line

dn: cn=colin, o=Your Company
changetype: delete
delete: attrccp
attrccp: value2

One minute MVS. Getting started with LDAP on ADCD.

LDAP is a standard protocol for accessing directory information over TCP/IP. For example the command

ldapsearch -h 127.0.0.1 -D “cn=Admin, o=Your Company” -w secret -b “o=Your Company” “(objectclass=*)” aclEntry

This sends a request to IP address 127.0.0.1 with userid cn=… and password “secret”, for information under the subtree of “o=Your Company” and requests is sends back information on any ACL entries.

z/OS implementation

LDAP on z/OS is also know as Tivoli Directory Server.

It can run with different backend databased from DB2 to a files in a USS directory. It can interface to RACF so you can use query userid and group information from RACF through LDAP.

Schemas.

You need to configure a schema of what fields there are, and the relationship. For example, for an organisation telephone directory you might have

dn: cn=LDAP Administrator, o=Your Company
objectclass: organizationalPerson
cn: LDAP Administrator
sn: Administrator
userPassword: ********
phoneNumber:1234567

Where

  • dn: cn=LDAP Administrator, o=Your Company This is the internal name of the object, and what part of the data tree it belongs to “o=Your Company
  • objectclass: organizationalPerson defines the object type
  • cn: LDAP Administrator This is the common name ( nick name) of the object
  • sn: Administrator This is the surname of the person
  • userPassword: ******** This is the user’s password. It has been defined that the value is not displayed
  • phoneNumber:1234567 This has been defined so that is can only take numbers and ‘-‘.

You can define your own attributes and properties. You just need to update the schema.

Which database is used?

A sample LDAP configuration might contain

database LDBM GLDBLD31/GLDBLD64
suffix “o=Your Company”
databaseDirectory /var/ldap/ldbm

  • database LDBM says there is a database in a USS directory
  • GLDBLD31/GLDBLD64 are the names of the interface routines to use.
  • suffix “o=Your Company” is the root of the subtree in this database
  • databaseDirectory /var/ldap/ldbm is the name of the USS directory

You can configure LDAP to say for these names(o=someoneElsesCompany) go to another LDAP with at this address.

If I use a query like ldapsearch -h 127.0.0.1 -D “cn=LDAP Administrator, o=Your Company” -w secret -b “o=Your Company” “(objectclass=*)” aclEntry…. the -D cn=LDAP Administrator, o=Your Company” says look for a userid with the given data in the o=Your Company subtree. With the above definitions it would look in the the USS file system under /var/ldap/ldbm, for a userid cn=LDAP Administrator, o=Your Company.

Configuring an LDAP server on ADCD.

ADCD is a preconfigured system which can on on zPDT and ZD&T. These provide a system 390 emulator. This system comes with a lot of software installed, and some subsystems such as z/OS, MQ, DB2, IMS, CICS and z/OSMF pre configured.

The software for LDAP(Tivoli Directory Server) is installed but not configured. The documentation is extensive, and the configuration file is very large (with lots of comments). You run a configuration script which produces some files.

However for a simple configuration you only need a few files to run.

Some of these files do not work – for example they try to define a userid with an existing Unix uid.

I’ve taken the updated files and put them on git hub.

The TDS documentation is here.

If you get into a mess you can just delete the /var/ldap/ldbm directory and start again!