FIPS, TLS 1.3, AT-TLS, z/OS and not connecting.

Or, My TLS connection just dies during the handshake – because of FIPS!

I was working with John M. on a problem connecting a client machine to talk to z/OS TN3270, and this identified some “interesting” holes.

  • The root cause is that on z/OS 3.1 and earlier AT-TLS does not support FIPS with TLS 1.3.
  • There is support in z/OS 3.2 for FIPS 140-3.
  • The cards in ICSF need to be configured for FIPS. If they are not configured, the sessions will fail with a trace entry in the CTRACE output saying “FIPS not supported” or some other vague message.
  • You can use the operator command D ICSF,CARDS to display the status.
  • You can use the ISPF panels.
    • In ISPF option 6 type the command @ICSF. This displays the ICSF main panel.
    • Option 1 COPROCESSOR MGMT
    • It displays your co-processors.
    • Use the S line command on the co-processors
    • If you get a message like FIPS Compliance Mode          : NOT SUPPORTED. You need to reconfigure your co-processors.
  • To configured FIPS, it is a destructive reset, and all master keys will be reset. This needs to be carefully planned.

Steps to solving the problem

You can use tools like Wireshark to display the traffic, and sometimes see why a TLS handshake fails.

Many of the problems I experienced were due to configuration problems on z/OS. I got a CTRACE trace on z/OS, see GSK trace and TCPIP and this usually allowed me to fix the problem.

Alert (40)

Alert Message:Level: Fatal (2): Description: Handshake Failure (40)

I used the gsksrvr ctrace to find that I did not have any TLS 1.3 certificates in my configuration.

Alert (51)

With TLS 1.3, A certificate like

SUBJECTSDN(CN('10.1.1.2') - 
O('NISTEC256') -
OU('SSS')) -
ALTNAME(IP(10.1.1.2))-
NISTECC -
KEYUSAGE( HANDSHAKE ) -
SIZE(256 ) -
SIGNWITH (CERTAUTH LABEL('DOCZOSCA')) -
WITHLABEL('NISTEC256')

Failed. But changing it to SIZE(512) worked. Even though size 256 is supported.

Using TLS 1.3, the handshake to TN3270 failed with no reason.

I tracked down some problems due to FIPS being enabled.

FIPS standards establish requirements for ensuring computer security and interoperability, and are intended for cases in which suitable industry standards do not already exist.

I think of FIPS as taking the existing standards and making them a bit more secure. For example not allowing some cipher suites. Not allowing certificates with small keys.

Enabling FIPS properly does not look easy. For example the documentation says it requires that load modules are cryptographically signed, so code authorised programs can check they have not been changed. Under the covers I believe that when IBM ships a module, it calculates the hash of the code, then encrypts the hash, and stores the encrypted has within the loadmodule. At runtime you use IBM’s public key to decrypt this value; does the same hash on the module, and compares this.

Once this has been done, you can add statements to the ICSF configuration, such as FIPSMODE(YES,FAIL(YES)).

This says use FIPS, and if any checking fails – fail the request.

In z/OS 3.2 there is FIPS support for TLS 1.3 see option FIPSMODE(140-3,INDICATE,FAIL(fail-option))

Not all configurations are supported

The TLS 1.3 ciipher suites, ChaCha20 and ChaCha20-Poly1305 are not supported by FIPS. You need to use cipher suites, configured with AES-GCM or AES-CCM.

I ran my test using FIPS

I could see in Wireshark that there was the TLS 1.3 trace

  • ClientHello request going to the server
  • ServerHello coming from the server
  • Change Cipher spec coming from the server
  • and nothing. No Alert message.

I found an entry in the z/OS 2.5 documentation.

The FIPS 140-2 standard does not define support for TLSv1.3 or the new cipher suites defined for it. Enabling both the TLSv1.3 protocol and FIPS support results in an error.

When my request failed I got CTRACE entries like

S0W1      MESSAGE   00000004  12:52:55.333904  SSL_ERROR                                  
Job TCPIP Process 0201001E Thread 00000001 crypto_chacha_encrypt_ctx
ICSF service failure: CSFPSKE retCode = 0x8, rsnCode = 0xbfe

S0W1 MESSAGE 00000004 12:52:55.334123 SSL_ERROR
Job TCPIP Process 0201001E Thread 00000001 crypto_chacha_encrypt_ctx
The algorithm or key size is not supported by ICSF FIPS

S0W1 MESSAGE 00000004 12:52:55.334355 SSL_ERROR
Job TCPIP Process 0201001E Thread 00000001 gsk_encrypt_tls13_record
ChaCha20 Encryption failed: Error 0x0335308f

Where the return code 0xbfe is

The PKCS #11 algorithm, mode, or keysize is not approved for ICSF FIPS 140-2. This reason code can be returned for PKCS #11 clear key requests when ICSF is in a FIPS 140-2 mode or 140-3,HYBRID mode. To see how 8/BFE(3070) can be returned when the ICSF FIPSMODE is 140-3,HYBRID, see ‘Requiring FIPS 140-2 algorithm checking from select z/OS PKCS #11 applications’ in z/OS Cryptographic Services ICSF Writing PKCS #11 Applications.

May the FIPS code is badly implemented, by not producing an alert message such as “FIPS processing problem”, but some security products to not display error information, because it makes it easier to break in!

Getting a CTRACE

Component TRACE (CTRACE) is the z/OS system trace capability for z/OS components. Most z/OS components use it.

From “capturing a trace” perspective, there are two aspects.

  • Capturing the trace data
    • The trace can be an in-memory trace, which is available when a dump is taken. This is is often the default. For example by default a trace is enabled to capture errors, and the in-memory trace is used.
    • You can have a trace writer started task which writes to a data set. When you start the trace you give the name of the started task. Data is passed to the trace writer job. You can then use the trace data set in IPCS.
  • Enabling the trace for the component. Usually there are options you can specify, for example all entries, or just error entries and how big the in-memory trace should be.

To trace a z/OS component, you need to know the CTRACE component name, and what you want to trace.

I tried to capture a CTRACE of a z/OS component, and struggled, because I didn’t know the name of the component.

What are the trace component names?

The z/OS command

TRACE STATUS         

gave

IEE843I 16.10.22  TRACE DISPLAY 940                               
SYSTEM STATUS INFORMATION
ST=(ON,0001M,00005M) AS=ON BR=OFF EX=ON MO=OFF MT=(ON,064K)
COMPONENT MODE COMPONENT MODE COMPONENT MODE COMPONENT MODE
--------------------------------------------------------------
CSF ON NFSC ON SYSGRS MIN SYSANT00 MIN
SYSJES2 SUB SYSRRS MIN SYSIEAVX MIN SYSSPI OFF
SYSJES SUB SYSHZS MIN SYSSMS OFF SYSAXR MIN
SYSDLF MIN SYSOPS MIN SYSXCF MIN SYSDUMP ON
SYSLLA MIN SYSXES ON SYSUNI OFF SYSCATLG MIN
SYSTTRC OFF SYSTCPDA SUB SYSRSM SUB SYSAOM MIN
SYSVLF MIN SYSTCPIP SUB SYSLOGR ON SYSOMVS MIN
SYSCEA MIN SYSWLM MIN SYSTCPIS SUB SYSTCPRE SUB
SYSIOS MIN SYSANTMN MIN SYSDMO MIN SYSIEFAL ON
SYSTCPOT SUB

I was after a CEA trace, and from the above, the name is SYSCEA. It is MIN, so is already active.

What is the trace’s status?

d trace,comp=SYSCEA

gave me

COMPONENT     MODE BUFFER HEAD SUBS                           
-------------------------------------------------------------
SYSCEA MIN 0002M
ASIDS *NONE*
JOBNAMES *NONE*
OPTIONS ERROR
WRITER *NONE*

So it is active, capturing errors, and writing to the in-memory trace (because there is no WRITER). I recognised the options as the defaults in parmlib member CTICEA00.

I had my own trace writer started task

Member CTWTR in proclib

//CTWTR PROC                                                                  
//DELETE EXEC PGM=IEFBR14
//TRCOUT01 DD DSNAME=IBMUSER.CTRACE1,
// SPACE=(CYL,(10),,CONTIG),DISP=(MOD,DELETE)
//*
//IEFPROC EXEC PGM=ITTTRCWR,TIME=999
//TRCOUT01 DD DSNAME=IBMUSER.CTRACE1,
// SPACE=(CYL,(10),,CONTIG),DISP=(NEW,CATLG)
//SYSPRINT DD SYSOUT=*

I started my CTRACE writer

TRACE CT,WTRSTART=CTWTR          

I created my own member CTICEACP in parmlib

TRACEOPTS 
ON
BUFSIZE(20m)
OPTIONS('ALL')
WTR(CTWTR)

The WTR ties up with my CTRACE writer started task name.

Stop the current trace

TRACE CT,OFF,COMP=SYSCEA

Start the CEA trace using my member

TRACE CT,ON,COMP=sysCEA,PARM=CTICEACP

Run the test

Stop the CEA trace

TRACE CT,OFF,COMP=SYSCEA

Stop the trace writer

TRACE CT,WTRSTOP=CTWTR     

The output from the CTWTR task gave me

IEF196I IEF142I CTWTR CTWTR - STEP WAS EXECUTED - COND CODE 0000         
IEF196I IGD104I IBMUSER.CTRACE1 RETAINED,
IEF196I DDNAME=TRCOUT01

which gives me the name of the data set IBMUSER.CTRACE1.

Use IPCS to look at the trace

  • option =0 to specify the name of the data set
  • =6
  • dropd
  • The above command clears out any old information about the data set
  • CTRACE COMP(SYSCEA) full

I had some data in the trace – but not for the problem I had…. so I need to try something else.

The advanced class.

You do not need to have a member in parmlib. You can use

TRACE CT,ON,COMP=SYSCEA

and do not specify a PARM. This will then prompt for the parameters, asid, jobname, writer and options.

Getting a trace from ICSF using CTRACE

I had a problem with a certificate and as part of trying to track down the problem, I traced all of the components. I found the documentation for tracing ICSF was not complete – it assumed you were going to take a dump.

You can collect a CTRACE from ICSF, and have it go to a CTRACE dataset.

Create the CTICSFxx member in parmlib

I copied CTICSF00, added the writer statement, and said collect ALL

TRACEOPTS 
          ON 
          BUFSIZE(2M) 
          wtr(ctwtr) 
          OPTIONS('ALL') 

Turn off the ICSF trace

TRACE CT,OFF,COMP=CSF   

Start the trace writing started task

See Create a procedure to collect the trace. The same trace writer can be used for all ctrace record.

TRACE CT,WTRSTART=CTWTR  

Start CTRACE

You need a parmlib member to contain the definitions I called mine CTICSF01

TRACEOPTS 
/*-----------------------------------------------------------------*/
/* ON OR OFF: PICK 1 */
/*-----------------------------------------------------------------*/
ON
/* OFF */
BUFSIZE(2M)
wtr(ctwtr)
/*-----------------------------------------------------------------*/
/* OPTIONS: NAMES OF FUNCTIONS TO BE TRACED, OR "ALL", OR "MIN" */
/*-----------------------------------------------------------------*/
/* OPTIONS( */
/* 'ALL' */
/* ,'KDSIO' */
/* ,'CARDIO' */
/* ,'SYSCALL' */
/* ,'DEBUG' */
/* ,'RDIO' */
/* ,'RDDATA' */
/* ,'MIN' */
/* ) */
OPTIONS('KDSIO','CARDIO','SYSCALL','RDIO','ALL')

Start the trace

TRACE CT,ON,COMP=CSF,PARM=CTICSF01

This gave me

TRACE CT,ON,COMP=CSF,PARM=CTICSF01
IEE252I MEMBER CTICSF01 FOUND IN USER.Z24C.PARMLIB
ITT038I ALL OF THE TRANSACTIONS REQUESTED VIA THE TRACE CT COMMAND WERE SUCCESSFULLY EXECUTED.

Run your failing transaction

Stop ctrace

TRACE CT,OFF,COMP=CSF 

This gave

ITT038I ALL OF THE TRANSACTIONS REQUESTED VIA THE TRACE CT COMMAND WERE SUCCESSFULLY EXECUTED.

Stop the trace writer

TRACE CT,WTRSTOP=CTWTR

This gave messages

ITT111I CTRACE WRITER CTWTR TERMINATED BECAUSE OF A WTRSTOP REQUEST.
IEF404I CTWTR – ENDED – TIME=07.54.33
IEF196I IEF142I CTWTR CTWTR – STEP WAS EXECUTED – COND CODE 0000
IEF196I IGD104I IBMUSER.CTRACE1 RETAINED

Any data is in the dataset.

Go into IPCS.

The instructions below show how to find statements with a non zero return code.

  • =0 – and enter the data set name
  • =6 – to get to the ipcs command screen
  • dropd – to tell IPCS to forget any information it may know about for the dataset
  • CTRACE COMP(CSF) full – this displays any ICSF CTRACE data
  • m PF8 – go to the bottom of the data
  • report view – to go into ISPF View mode on the data set
  • X ALL – to hide all of the data
  • f code all – this shows any error codes
  • c ‘Return code = 00000000 ‘Return ZZZZ = 00000000’ all – use cut and paste of the string, as blanks are lost in this post
  • c ‘Condition code = 00000000’ ‘Condition ZZZZ = 00000000’ all
  • x all
  • f code all – this will show you are statements with a non zero return code.