This started as a mini project before lunch, and now, two weeks later, I have got all of the bits to work.
As I was struggling with this, I wondered why no one had crossed this swamp before me. Is it because no one wants to cross it, or it is too difficult to do so? I think it was just difficult to do so – but I can now provide a map!
It took me a couple of hours to get LDAP on z/OS to use TLS from my Linux machine. The challenge was finding the right options. I then tried using Elliptical Certificates, and got this working, then was partially successful in using TLS 1.3.
As part of this I wrote Authentication using certificates with LDAP, which is where it was really difficult.
For clients, I used ldapsearch (from openldap ) on Ubuntu, and ldapsearch on z/OS.
See the product documentation here.
I’ve put a section “What cipher specs should I use” in a separate blog post.
Configure the LDAP environment file
I had to add TLS V1_2 and TLSV1_3 (and disable the deprecated TLS 1.1)
to the environment file, because they default to off. Without these options it defaults to SSL – which is so old, my certificates and keys did not support it. I disabled TLS_1_1 because it is old.
This is used when validating certificates. The signature algorithm of the CA must be in this list see GSK_TLS_SIG_ALG_PAIRS parameter. I found it easiest to specify most of the available options 0601060305010503040104030402″. (0601, 0603…) (see here), get things working, then remove the ones I did not want to use; rather than try to add the ones I wanted to use.
I initially added
This is a list of the cipher specs it will accept. You should try to restrict the certificates in your organisation, to Elliptical Certificates, and a limited configuration for any CA’s. For example have
- all client’s certificates defined as Elliptic Curve type prime256v1, signed with ecdsa-with-SHA256.
- All internal CAs defined as Elliptic Curve type prime256v1 signed with ecdsa-with-SHA256.
When using certificate authentication I needed the following for it all to work
The environment variables
these are used only when using TLS 1.3. I’ll blog about using TLS 1.3 at a later date.
While you are investigating problems you can use the environment variable GSK_TRACE=0xff to write a trace. You can specify GSK_TRACE_FILE to direct the trace to a particular file. The default location is /tmp/gskssl.%.trc where % is the thread id. You format it with
gsktrace /tmp/gskssl.65567.trc > out
So I could have a shell script to format and display the trace.
gsktrace gsktrace.ldap > out
Change the LDAP configuration file.
sslMapCertificate check fail
I specified a port for ldaps. You can use
ldapsearch -H ldaps://10.1.1.2:389
This is preferred to specifying host and port
ldapsearch -h 10.1.1.2 -p 389
A port can process TLS and non-TLS requests, so definition for port 1389 may not needed.
I specified a keyring sslKeyRingFile START1/MQRING. This is owned by a different userid to the started task, so needs more RACF definitions. See below for the definitions.
Define the keyring and keyring
I defined an RSA certificate and an EC certificate. I controlled which was used from the configuration file.
* create the ring RACDCERT DELRING(MQRING) ID(START1) RACDCERT ADDRING(MQRING) ID(START1) SETROPTS RACLIST(DIGTCERT,DIGTRING ) refresh * and the server's RSA certificate RACDCERT ID(START1) DELETE(LABEL('ZZZZ')) RACDCERT ID(START1) GENCERT - SUBJECTSDN(CN('ZZZZ') - O('SERVER') - OU('SSS')) - ALTNAME(IP(10.1.1.2))- SIZE(4096)- SIGNWITH (CERTAUTH LABEL('COLIN-CA')) - RSA - WITHLABEL('ZZZZ') - KEYUSAGE( DATAENCRYPT,DOCSIGN,HANDSHAKE ) RACDCERT id(START1) ALTER(LABEL('ZZZZ'))TRUST RACDCERT ID(START1) CONNECT(RING(MQRING) - ID(START1) - LABEL('ZZZZ') DEFAULT) SETROPTS RACLIST(DIGTCERT,DIGTRING ) refresh * and the Elliptic Certificate RACDCERT ID(START1) DELETE(LABEL('SERVEREC')) RACDCERT ID(START1) GENCERT - SUBJECTSDN(CN('SERVEREC') - O('ADCD') - OU('TEST')) - ALTNAME(IP(10.1.1.2)) - SIZE(521) - NISTECC - SIGNWITH (CERTAUTH LABEL('COLIN-CA')) - KEYUSAGE(HANDSHAKE ,KEYAGREE) - NOTAFTER( DATE(2024-12-29))- WITHLABEL('SERVEREC') RACDCERT id(START1) ALTER(LABEL('SERVEREC'))TRUST RACDCERT ID(START1) CONNECT(RING(MQRING) - ID(START1) - LABEL('SERVEREC') DEFAULT) SETROPTS RACLIST(DIGTCERT,DIGTRING ) refresh RACDCERT LISTRING(MQRING) ID(START1)
Permit the LDAP server’s userid to access the private certificate in the keyring
Because the LDAP userid does not own the keyring, the server userid needs update access to be able to extract the private key.
RDEFINE RDATALIB START1.MQRING.LST UACC(NONE) PERMIT START1.MQRING.LST CLASS(RDATALIB) ID(GLDSRV) - ACCESS(CONTROL) SETROPTS RACLIST(RDATALIB) REFRESH
For a userid to be able to use its own keyring, it only need read access to be able to use the private certificate.
Set up the client userid
I set up the userid in the z/OS LDAP server, so I could use the z/OS userid and password. See here.
Test it out
LDAP checks the TLS parameters, when a TLS request arrives. It does not check at startup . I used openssl s_client to connect, and display information about the TLS handshake. See Debugging TLS – an easier way.
Setting up the client on Ubuntu
I set up a file ldaprc containing the ldap configuration. You can enter all of the parameters on the command line, but the ldaprc file makes it easier. My file had
The #TLSCERT etc are ignored. No certificate is sent to the server (unless the -Y external option is set).
Enable GSK trace on z/OS
I enabled an LDAP trace to //SYSPRINT on z/OS, using f GLDSRV,debug error+CONNS+LDAPBE
- error reports errors
- Conns reports connection information, such as the cipher spec used.
- LDAPBE reports on the communication to the back end, for example it reports the SAFUSER
Run a query on Linux
I used ldapsearch on Ubuntu
ldapsearch -H ldaps://10.1.1.2:1389 -D “cn=ibmuser, o=Your Company” -w panthe0n -b “o=Your Company” “(objectclass=*)” aclEntry
The trace on z/OS GLDSRV included
CONNS srv_ssl_connect()644: SSL Cipher Specs
00000000: c3f0f0f8 *C008 *
and we can see that SSL was used, and cipher spec C008 was used.
Because I had set up my userid to map use a SAF userid, in the trace I had
LDAPBE srv_process_bind_request()939: do_return_bind msgID=1, connID=8, bindDN=‘CN=IBMUSER, O=YOUR COMPANY’, safUserID=’IBMUSER’, dnList=0x0, grpList=0x0, rc=0