Getting the z/OS standard image to work.

I had a lot of challenges getting the zD&T replacement (the standard image) to work. I do not know if this is the same image that every one else gets, or if mine was “a special” one. Some things did not work, somethings were not best practice.

I also wanted to use the data from my previous systems, my PDS, my Unix file systems, my keyrings etc. I also wanted to future proof migrating to the next code drop from IBM.

Summary of actions

Below is a list of the things I did to get the system up and working as I expected it to look.

I’ve grouped them by area. In the main body of the document, they are in the approximate order of execution

Problems

Customising

Before you start using it

Missing download and setting the IBMUSER password

The files on the IBM Passport Advantage site are incomplete. The documentation tells you do download the ZDTDPY volume, edit it to set the password, and boot the system. This download was not on the download site.

I had an old z/OS system I could use and so could reset the password. See Help, I cannot logon to my z/OS system

It is not difficult to write a C program to prompt for a password on the z/OS console – and have the value not displayed. See How do I enter a password on the z/OS console for my program?

I needed to configure my laptop

The IP address of z/OS is 172.26.1.2. I configured my startup script to have

#  define route from my laptop to my server
sudo ip route add 10.1.1.0/24 via 10.1.0.3

# define route from my laptop to z/OS on the server
sudo ip route add 172.26.1.0/24 via 10.1.0.3

# connect to the server
ssh -X colin@10.1.0.3

The sample devmap is wrong, it does not provide 3270’s

The system would not IPL with a console, the output came out printer like.

I edited the devmap file:

  • add the 3270port 3270. Without this you do not get any 3270’s defined, and you get a line printer like console.
  • Change the processors to match what your server has. My dongle has support for 3 CPUs – but I can define 3-1 ZIIPs
  • Check the memory to match you configuration
  • I removed the IPL and put it in my shell script.

The top of my devmap is

[system]
processors 5 cp cp cp ziip ziip # number of processors
memory 10G
system_name VS01

3270port 3270 # port number for TN3270 connections

First IPL

The IPL parameters are configured to start up lots of subsystems I didn’t want to use I used

ipl DE27 parm DE28AU

Logon with userid IBMUSER and the password you configured.

Configure parmlib

The sys0.iplparm points to loadxx members. You need a load member with USER.PARMLIB in it (such as LOADAU)

  • Copy LOADAU to LOADCP
  • In LOADCP change SYSPARM to SYSPARM AU,CP
  • In user.parmlib create IEASYSCP (CP matching the CP above)
  • add your parameters, such as OMVS=(CP) for IP(V6) support

You can IPL with this

ipl DE27 parm DE28CP

If this fails to IPL, to back to parm DE28AU

Add userids

The userid IBMUSER is all powerful. I prefer using a less powerful userid.

I wanted to save my userids from the previous system. I wrote code makeRACF. With this you run a program on your old system, and specify a userid or dataset prefix. The program then generates the RACF commands to recreate the userid or resource.

Because the TSO configuration is different from the previous zD&T systems I had to configure the definitions

TSO (ACCTNUM ('ACCT001') - 
COMMAND ('ex ''colin.zlogon.clist''') -
PROC (PROC001 ) -
SIZE (2096128) -
MAXSIZE (2096128) -
USERDATA (0000) -
UNIT (3390))

I created some definitions to my userid COLIN access to resources on the new system

ALTUSER COLIN SPECIAL AUDITOR OPERATIONS                             
ALTUSER COLIN DFLTGRP(SYS1)
ALTUSER COLIN tso( ACCTNUM('ACCT001') -
CoMMAND('ISPF') PROC(PROC001 ) -
SIZE(2096128) MAXSIZE(2096128) USERDATA(0000) UNIT(SYSDA))
permit ACCT001 class(ACCTNUM) id(COLIN ) access(READ )
permit ACCT# class(ACCTNUM) id(COLIN ) access(READ )
permit JCL class(TSOAUTH)id(COLIN) access(REAd)
permit CONSOLE class(TSOAUTH)id(COLIN) access(REAd)
permit PROC001 class(TSOPROC)id(COLIN) access(REAd)
permit ISPFPROC class(TSOPROC)id(COLIN) access(REAd)
setropts raclist(TSOAUTH) refresh
setropts raclist(ACCTNUM) refresh
setropts raclist(TSOPROC) refresh
PERMIT *.** CLASS(JESSPOOL) ID(COLIN) ACCESS(ALTER)
SETR RACLIST(JESSPOOL) REFRESH

I created JCL for these definitions, so for system refreshes, I just rerun the jobs.

I did not use the UNIX facility to allocate a (random) UID. I specified the UID I had before.

On the ZFS with my files, I needed the uid of the file owner to be the same as my users uid. If z/OS allocated me a uid, I would not be able to access my files. I would then have to go through and change the owner of them.

I recreated userids COLIN and START1.

You need to see what groups the userids belonged to, and you may need to recreate the groups, or just us what are already defined.

Import user catalogs

I have user catalogs for all of my data sets. These catalogs need to be imported into the master catalog

//IBMUSERT JOB 1,MSGCLASS=H                                           
//S1 EXEC PGM=IDCAMS,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
IMPORT CONNECT -
OBJECTS -
(('A4USR1.ICFCAT' VOLUME(A4USR1) DEVICETYPE(3390) -
))
/*

How do you find out what user catalogs you have?

On the old system.

//IBMUSERT JOB 1,MSGCLASS=H                               
//S1 EXEC PGM=IDCAMS,REGION=0M
//SYSPRINT DD SYSOUT=*
//DD1 DD DISP=SHR,VOL=SER=D5SYS1,UNIT=3390
//SYSIN DD *
LISTCAT CATALOG(CATALOG.Z31B.MASTER) USERCATALOG ALL FILE(DD1)
/*

or

//IBMUSERT JOB 1,MSGCLASS=H                               
//S1 EXEC PGM=IDCAMS,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
LISTCAT USERCATALOG ALL
/*

The ALL Parameter also lists the Aliases using the user catalog.

Define aliases to map high level qualifier to a user catalog.

Once you have imported the catalogs you can define the data set aliases

//IBMUSERT JOB 1,MSGCLASS=H                                     
//S1 EXEC PGM=IDCAMS,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEFINE ALIAS (NAME(BACKUP) RELATE('A4USR1.ICFCAT') )
DEFINE ALIAS (NAME(COLIN ) RELATE('A4USR1.ICFCAT') )
/*

What aliases do I have?

//IBMUSERT JOB 1,MSGCLASS=H                                         
//S1 EXEC PGM=IDCAMS,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
LISTCAT ALIAS
/*

Define user directories

For each file system, and each user, they need a directory defined. The standard image uses automount to create a ZFS for each entry in /u. If my RACF profile says my home directory is /u/colin, it will create a ZFS for this. If /u/colin already existed, it will not be used.

I created entries in /usr. If you try to make a directory, it may fail because /usr is mounted read only.

From userid IBMUSER to into Unix ( TSO OMVS)

  • mkdir /usr/colin
  • If this fails use
    • chmount -w /usr … do your work … chmount -r /usr
  • mkdir /usr/tmp
  • mkdir /usr/tmp/zowet
  • mkdir /usr/tmp/zowet/colin Userid COLIN has HOME= /usr/tmp/zowet/colin
  • You may need to change the ownership of the directory
    • chown -R colin:sys1 mkdir /usr/tmp/zowet/colin
  • If you need to…
    • chmount -r /usr

Mount the ZFS

Once I had imported the catalogs, and defined the aliases I could try to mount my ZFS

I created USER.PARMLIB(BPXPRMUS) and added

MOUNT FILESYSTEM('COLIN.ZFS.ZOWE.TEMP') TYPE(ZFS) 
MOUNTPOINT('/usr/tmp/zowet') MODE(RDWR)
PARM('AGGRGROW') AUTOMOVE

MOUNT FILESYSTEM('COLIN.ZOPEN.ZFS') TYPE(ZFS)
MOUNTPOINT('/usr/zopen') MODE(RDWR)
PARM('AGGRGROW') AUTOMOVE

The activate using the operator command

T OMVS=(US)

I was then able to logon to my newly created userid COLIN, and go into z/OS Unix.

Configure VTAM to give you greater than 80 *24 screen size

VTAM configuration

  • Edit SYS1.VTAMLST(EXLOCAL). You have to edit this because NET does not have USER.VTAMLST configured.
  • Create EXLOCALO from it (for backup)
  • change DLOGMOD to have value D4B32XX3,
  • create an entry for CUADDR=061,

Clean up

  • Edit SYS1.VTAMLST(ATCCON00). Remove IVPLCLI,IVPLCLT because the devices they reference do not exist.

If your changes to EXLOCAL do not work, use

V NET,ACT,ID=EXLOCALO

TCPIP

The provided TCPIP procedure creates certificates etc and does other work. It creates a new CA certificate, I need to export it, and sent it to my machines which connect in, and to all my keyrings.

I want to use the set up I have, so my TCPIP V6 Procedure is

USER.PROCLIB(TCPIP)

//TCPIP    PROC PARMS='CTRACE(CTIEZB00)' 
//TCPIP EXEC PGM=EZBTCPIP,PARM='&PARMS',REGION=0M,TIME=1440
//SYSPRINT DD SYSOUT=*
//SYSERR DD SYSOUT=*
//SYSERROR DD SYSOUT=*
//ERRORFIL DD SYSOUT=*
//SYSDEBUG DD SYSOUT=*
//PROFILE DD DISP=SHR,DSN=USER.TCPPARMS(PROFILE)
//SYSTCPD DD DISP=SHR,DSN=TCPIP.TCPPARMS(TCPDATA)

Support TCPIP V6

The TCPIP as shipped does not support IP V6. You need to create a BPXPRMxx member and activate it

You need a BPXPRMv6 member in user.parmlib

FILESYSTYPE TYPE(CINET) 
ENTRYPOINT(BPXTCINT)
SUBFILESYSTYPE NAME(TCPIP)
TYPE(CINET)
ENTRYPOINT(EZBPFINI)
DEFAULT
NETWORK DOMAINNAME(AF_INET6)
DOMAINNUMBER(19)
MAXSOCKETS(50000)
TYPE(CINET)

In USER.PARMLIB(IEASYSXX) use BPX=(V6).

In USER.TCPPARMS(PROFILE) I have

...
DATASETPREFIX TCPIP

TCPCONFIG TTLS
INCLUDE USER.TCPPARMS(IPV6)

...

and the IPV6 member has

IPCONFIG6 MULTIPATH

TCPIP Resolver

See Why is the wrong TCPIP Resolver proc being used?

Create USER.PROCLIB(RESOLVER)

//* TCPIP RESOLVER - COLINS 
//*
//RESOLVER PROC PARMS=CTRACE(CTIRES00)
//*
//EZBREINI EXEC PGM=EZBREINI,REGION=0M,TIME=1440,
// PARM=('&PARMS',
// 'ENVAR("RESOLVER_TRACE=/var/log/resolver"/')
//SETUP DD DISP=SHR,DSN=COLIN.TCPPARMS(GBLRESOL),FREE=CLOSE
//SYSTCPT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//*

Create COLIN.TCPPARMS(GBLRESOL) with

Copy

  DEFAULTTCPIPDATA('COLIN.TCPPARMS(GBLTDATA)') 
GLOBALTCPIPDATA('COLIN.TCPPARMS(RESOLVE)')
# GLOBALTCPIPDATA(/etc/resolv.conf)
;
# -----------------------------------------------------------------
# Default zPDT Linux Base to z/OS Tunnel (Stand-Alone)
# -----------------------------------------------------------------
;
# GLOBALIPNODES(/etc/hosts)
GLOBALIPNODES('COLIN.TCPPARMS(HOSTS)')
....

Change the operator console PFKeys (to add the shutdown command)

See How do I change the PFKeys on the console?

Copy SYS1.PARMLIB(PFKTAB00) to USER.PARMLIB

  • Change PF12 to be PFK(12) CMD(“%NETV SHUTSYS”) CON(Y)
  • Change any other keys you fancy.

Fix SMF

Message HSF0066W

I was getting

HSF0066W Required exit IEFU86 for SMF subsystem SYS not enabled. Some data may be missing in SDSF event log.

Copy SYS1.PARMLIB(SMFPRM00) to USER.PARMLIB(SMFPRM00) add IEFU86

SYS(EXITS(IEFACTRT,IEFUJI,IEFU83,IEFU84,IEFU85,IEFUJV,IEFU86)), 
SUBSYS(STC,EXITS(IEFU83,IEFU84,IEFU85,IEFU29,IEFU86),
INTERVAL(SMF,SYNC))

Message IEE391A

IEE391A SMF ENTER DUMP FOR DATA SET ON VOLSER OPEVS1 DSN=SYS1.VS01.MAN1

Create in USER.PROCLIUB(SMFCLEAR)

//SMFCLEAR PROC MAN='SYS1.VS01.MAN1' 
//*
//* CLEAR SMF DATASET -
//* SMF MAN DATASET DUMPED REQUIRED MESSAGE RECIEVED
//* OR ISSUE SWITCH, I SMF, THEN THAT FILE WILL BE CLEARED
//*
//*
//DUMP1 EXEC PGM=IFASMFDP,REGION=1M
//INDD1 DD DSN=&MAN,DISP=SHR
//DUMPOUT DD DUMMY
//SYSPRINT DD SYSOUT=D
//SYSIN DD *
INDD(INDD1,OPTIONS(CLEAR))

Then you can use either of

S SMFCLEAR
S SMFCLEAR,MAN='SYS1.VS01.MAN2'

SETSMF command not enabled

I could not issue

setsmf recording(logstream)
setsmf recording(dataset)

setsmf recording(logstream)

commands, to be able to change the SMF LOGSTREAM.

I created USER.PARMLIB(SMFPRM00) from SYS1.PARMLIB and added AUTHSETSMF – it defaults to NONE.

...
DEFAULTLSNAME(IFASMF.VS01.DATA)
AUTHSETSMF
NOPROMPT,
REC(PERM),
,,,

Configure SYSLOGD

Define the started task

//IBMUSERT JOB 1,MSGCLASS=H 
//S1 EXEC PGM=IKJEFT01,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
RDEFINE STARTED SYSLOGD.* STDATA(USER(IBMUSER))
SETR RACLIST(STARTED) REFRESH

SYSLOGD is used by many Unix processes for recording errors and other information.

Create USER.PROCLIB(SYSLOGD)

//SYSLOGD PROC 
//* Licensed Materials - Property of IBM *
//* "Restricted Materials of IBM" *
//* 5650-ZOS *
//* Copyright IBM Corp. 1992, 2013 *
//* Read parms from /etc/syslog.conf
//CONFHFS EXEC PGM=SYSLOGD,REGION=4096K,TIME=NOLIMIT,
// PARM='ENVAR(''CEE_ENVFILE_S=DD:STDENV'')/-c -i '
//STDENV DD DISP=SHR,DSN=USER.PROCLIB(SYSLOGDD)
//SYSPRINT DD SYSOUT=*
//SYSIN DD DUMMY
//SYSERR DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//CEEDUMP DD SYSOUT=*

Create USER.PROCLIB(SYSLOGDD)

I have

*.INETD*.*.*       /var/log/inetd.log 
auth.* /var/log/auth.log
mail.* /var/log//mail -F 640 -D 770
local1.err /var/log/local1.log
*.err /var/log/errors.log
# *.CPAGENT.*.* /var/log/CPAGENT.log
*.CPATTLS.*.* /var/log/CPATTLS
*.TTLS*.*.* /var/log/TTLS.log
*.Pagent.*.* /var/log/Pagent.log
*.TCPIP.*.debug /var/log/TCPIPdebug.log
*.TCPIP.*.warning /var/log/TCPIP.log
*.TCPIP.*.err /var/log/TCPIPerr.log
*.TCPIP.*.info /var/log/TCPIPinfo.log
*.SYSLOGD*.*.* /var/log/syslogd.log
*.TN3270*.*.* /var/log/tn3270.log
*.SSHD*.*.* /var/log/SSHD.log
*.FTPD*.*.* /var/log/FTPD.log
daemon.debug /var/log/SSHDdebug.log
*.TCPIP.*.none;
*.err /var/log/errors
*.CPAGENT.*.* /var/log/CPAGENT.log
*.TRMD1.*.info /var/log/TRMD1I.log
*.DMD.*.* /var/log/DMD.log

You can use this as a basis and change it as needed.

Configure SSHD

The SSHD procedure in SYS1.PROCLIB, invokes a shell script which then spawns the SSHD code with a name like SSHD.

I use

//SSHD    PROC 
//SSHD EXEC PGM=BPXBATCH,REGION=0M,TIME=NOLIMIT,
// PARM='PGM /usr/sbin/sshd -f /etc/ssh/sshd_config '
//* PARM='PGM /bin/sh -c /etc/ssh/sshd.sh'
//* STDIN AND STDOUT ARE BOTH DEFAULTED TO /dev/null
//STDERR DD PATH='/tmp/sshd.stderr',PATHOPTS=(OWRONLY,OCREAT,OAPPEND),
// PATHMODE=(SIRWXU)
//STDOUT DD PATH='/tmp/sshd.stdout',PATHOPTS=(OWRONLY,OCREAT,OAPPEND),
// PATHMODE=(SIRWXU)

Note that SSHD uses the /etc/ssh/sshd_config directory. You should back this up regulary. When IBM replaces the image this would be replace

After thought, I could always use -f /usr/colin/ssh/

Edit the configuration file and add either userids or groups.

# Allow specific user IDs 
AllowUsers IBMUSER COLIN
AllowGroups SYS1 IZUADMIN

If you add groups and not userids, you need to connect the userid to a group.

RACF changes

You can issue some racf commands on the z/OS console for example

<RVARY

To change this character (<) copy SYS1.PARMLIB(IEFSSN00) to USER.PARMLIB(IEFSSN01) and change the entry for RACF.

Define IXGLOGR as a started task to eliminate security messages

//IBMIXL  JOB 1,MSGCLASS=H 
//STEPNAME EXEC PGM=IKJEFT01
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
* IXGLOGR EXISTS AS A GROUP - IT REALLY SHOULD BE A USERID
ADDUSER IXGLUSER
CONNECT IXGLUSER GROUP(IXGLOGR)
RDEFINE STARTED IXGLOGR.* STDATA(USER(IXGLUSER) +
PRIVILEGED(YES) TRUSTED(YES) GROUP(IXGLOGR ))
SETROPTS RACLIST(STARTED) REFRESH
/*

Add missing groups

To protect datasets such as SYS1.*, the high level qualifier should be defined as a group. I have JCL ADDGROUP

//IBMKEY2 JOB 1,MSGCLASS=H 
//STEPNAME EXEC PGM=IKJEFT01
//SYSTSIN DD *
ADDGROUP SYS1
ADDGROUP ADCD
ADDGROUP AOK
ADDGROUP AOP
ADDGROUP AZD
ADDGROUP BBL
ADDGROUP CATALOG
ADDGROUP CBC
ADDGROUP CEE
ADDGROUP CFZ
ADDGROUP CNJ
ADDGROUP COUPLE
ADDGROUP CSD
ADDGROUP CSF
ADDGROUP CSQARC1
ADDGROUP CSQARC2
ADDGROUP EUVF
ADDGROUP FEU
ADDGROUP FEU1
ADDGROUP FFST
ADDGROUP GDDM
ADDGROUP GIM
ADDGROUP GLD
ADDGROUP GSK
ADDGROUP GTFNO
ADDGROUP GTFRACF
ADDGROUP HAP
ADDGROUP HLA
*DDGROUP IBMUSER
ADDGROUP ICQ
ADDGROUP IOE
ADDGROUP ISF
ADDGROUP ISP
ADDGROUP IXGLOGR
ADDGROUP IZU
ADDGROUP IZUSVR
ADDGROUP NETVIEW
ADDGROUP NFS
ADDGROUP SMPE
ADDGROUP STCJMON
ADDGROUP SYT1
ADDGROUP TCPIP
ADDGROUP USER
ADDGROUP ZFS
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=* g

Protect system data sets

You should define profiles for all datasets. I had a a member ADDSD.

//IBMKEY2 JOB 1,MSGCLASS=H 
//STEPNAME EXEC PGM=IKJEFT01
//SYSTSIN DD *
ADDSD 'SYS1.**' GENERIC UACC(READ) WARNING
PERMIT 'SYS1.**' ACCESS(ALTER) ID(SYS1)
ADDSD 'ADCD.**' GENERIC UACC(READ) WARNING
ADDSD 'AOK.**' GENERIC UACC(READ) WARNING
ADDSD 'AOP.**' GENERIC UACC(READ) WARNING
ADDSD 'AZD.**' GENERIC UACC(READ) WARNING
ADDSD 'BBL.**' GENERIC UACC(READ) WARNING
ADDSD 'CATALOG.**' GENERIC UACC(READ) WARNING
ADDSD 'CBC.**' GENERIC UACC(READ) WARNING
ADDSD 'CEE.**' GENERIC UACC(READ) WARNING
ADDSD 'CFZ.**' GENERIC UACC(READ) WARNING
ADDSD 'CNJ.**' GENERIC UACC(READ) WARNING
ADDSD 'COUPLE.**' GENERIC UACC(READ) WARNING
ADDSD 'CSD.**' GENERIC UACC(READ) WARNING
ADDSD 'CSF.**' GENERIC UACC(READ) WARNING
ADDSD 'CSQARC1.**' GENERIC UACC(READ) WARNING
ADDSD 'CSQARC2.**' GENERIC UACC(READ) WARNING
ADDSD 'EUVF.**' GENERIC UACC(READ) WARNING
ADDSD 'FEU.**' GENERIC UACC(READ) WARNING
ADDSD 'FEU1.**' GENERIC UACC(READ) WARNING
ADDSD 'FFST.**' GENERIC UACC(READ) WARNING
ADDSD 'GDDM.**' GENERIC UACC(READ) WARNING
ADDSD 'GIM.**' GENERIC UACC(READ) WARNING
ADDSD 'GLD.**' GENERIC UACC(READ) WARNING
ADDSD 'GSK.**' GENERIC UACC(READ) WARNING
ADDSD 'GTFNO.**' GENERIC UACC(READ) WARNING
ADDSD 'GTFRACF.**' GENERIC UACC(READ) WARNING
ADDSD 'HAP.**' GENERIC UACC(READ) WARNING
ADDSD 'HLA.**' GENERIC UACC(READ) WARNING
ADDSD 'IBMUSER.**' GENERIC UACC(READ) WARNING
ADDSD 'ICQ.**' GENERIC UACC(READ) WARNING
ADDSD 'IOE.**' GENERIC UACC(READ) WARNING
ADDSD 'ISF.**' GENERIC UACC(READ) WARNING
ADDSD 'ISP.**' GENERIC UACC(READ) WARNING
ADDSD 'IXGLOGR.**' GENERIC UACC(READ) WARNING
ADDSD 'IZU.**' GENERIC UACC(READ) WARNING
ADDSD 'IZUSVR.**' GENERIC UACC(READ) WARNING
ADDSD 'NETVIEW.**' GENERIC UACC(READ) WARNING
ADDSD 'NFS.**' GENERIC UACC(READ) WARNING
ADDSD 'SMPE.**' GENERIC UACC(READ) WARNING
ADDSD 'STCJMON.**' GENERIC UACC(READ) WARNING
ADDSD 'SYT1.**' GENERIC UACC(READ) WARNING
ADDSD 'TCPIP.**' GENERIC UACC(READ) WARNING
ADDSD 'USER.**' GENERIC UACC(READ) WARNING
ADDSD 'ZFS.**' GENERIC UACC(READ) WARNING
PERMIT 'ADCD.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'AOK.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'AOP.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'AZD.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'BBL.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'CATALOG.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'CBC.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'CEE.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'CFZ.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'CNJ.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'COUPLE.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'CSD.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'CSF.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'CSQARC1.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'CSQARC2.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'EUVF.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'FEU.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'FEU1.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'FFST.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'GDDM.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'GIM.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'GLD.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'GSK.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'GTFNO.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'GTFRACF.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'HAP.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'HLA.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'IBMUSER.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'ICQ.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'IOE.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'ISF.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'ISP.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'IXGLOGR.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'IZU.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'IZUSVR.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'NETVIEW.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'NFS.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'SMPE.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'STCJMON.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'SYT1.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'TCPIP.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'USER.**' ACCESS(ALTER) ID(SYS1)
PERMIT 'ZFS.**' ACCESS(ALTER) ID(SYS1)
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*

You should have started tasks defined

Started tasks should be defined, so end users cannot just create a Started Task, and run it as an authorised task.

Define a default

//IBMKEY2 JOB 1,MSGCLASS=H 
//STEPNAME EXEC PGM=IKJEFT01
//SYSTSIN DD *
RDEFINE STARTED *.* OWNER(IBMUSER) +
DATA('GENERIC DEFINITION - COLIN') +
STDATA( USER(=MEMBER)
SETROPTS RACLIST(STARTED) REFRESH

Export certificates and keyrings

I have member EXPRING. this runs the LRING Rexx which creates a data set containing certificates used by the userid. They are stored in &USERID..CERTS.START1

//IBMKEYR JOB 1,MSGCLASS=H 
//STEPNAME EXEC PGM=IKJEFT01,PARM='LRING START1',REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSEXEC DD DISP=SHR,DSN=USER.Z31B.CLIST
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD DUMMY

Import certificates and keyrings

Use the Rexx program to export certificates from your old system, and import them into the new system.

Import

//IBMKEYR JOB 1,MSGCLASS=H
//STEPNAME EXEC PGM=IKJEFT01,PARM='IRING COLIN.CERTS.START1',
// REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSEXEC DD DISP=SHR,DSN=USER.Z31B.CLIST,UNIT=3390,VOL=SER=B3CFG1
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD DUMMY

RRS Error messages

At the start up of RRS I got

ATR132I RRS LOGSTREAM CONNECT HAS FAILED FOR
– OPTIONAL LOGSTREAM ATR.VS01.ARCHIVE.
– RC=00000008, RSN=0000080B

See here

Issue the operator command

SETRRS ARCHIVELOGGING,DISABLE 

ICSF

ICSF

I have my own (I)CSF data sets, containing my keys. My Started Task JCL is the same as the default, but with a different member

//CSF  PROC PRM=CP 
//CSF EXEC PGM=CSFINIT,PARM=&PRM,REGION=0M,TIME=1440,MEMLIMIT=NOLIMIT

You could use the provided JCL, then stop and restart CSF

P CSF
S CSF,PRM=CP

The PARM=CP points to a member CSFPRMCP in USER.PARMLIB. Mine has

CKDSN(COLIN.SCSFCKDS) 
PKDSN(COLIN.SCSFPKDS)
TKDSN(COLIN.SCSFTKDS) qq
DOMAIN(0)
SSM(YES)
KEYARCHMSG(YES)

If this member does not exist you get an abend

IEF764I CSF CSF IEFPARM CSFMIOPD HCR77F0 PARMLIB READ FAILED - MEMBER CSFPRMCP NOT FOUND.        
CSFO0016 ERROR OCCURRED OPENING OPTIONS FILE. MEMBER CSFPRMCP IN DDNAME IEFPARM RC=12 RS=1.
...
DUMP TITLE=COMPON=CSF...ABEND=S018F,REASON=0000001B

Logrec EREP

When LOGREC fills up, you get a retained message on the console.

You can print the contents of the EREP file using

//PRINTLOG JOB (ACCT),'PRINT LOGREC',CLASS=A,MSGCLASS=H           
//STEP EXEC PGM=IFCEREP1,PARM='CARD'
//SERLOG DD DISP=SHR,DSN=VSPROV.VS01.LOGREC
//DIRECTWK DD UNIT=SYSDA,SPACE=(CYL,10,,CONTIG)
//EREPPT DD SYSOUT=A,DCB=BLKSIZE=133
//TOURIST DD SYSOUT=A,DCB=BLKSIZE=133
//ZERLOG DD SYSOUT=A,DCB=BLKSIZE=133
//SYSIN DD *
PRINT=PS
ACC=N
TYPE=S
ENDPARM
/*

and clear it using a procedure in USER.PROCLIB(CLLOGREC)

//CLLOGREC  PROC 
//STEP1 EXEC PGM=IFCDIP00
//SERERDS DD DISP=OLD,DSN=VSPROV.VS01.LOGREC,
// UNIT=3390,VOL=SER=OPEVS1

and the started task definition in STLOGREC

//IBMUSERT JOB 1,MSGCLASS=H 
//S1 EXEC PGM=IKJEFT01,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
RDEFINE STARTED CLLOGREC.* STDATA(USER(IBMUSER))
RDEFINE STARTED PRLOGREC.* STDATA(USER(IBMUSER))
SETR RACLIST(STARTED) REFRESH

JAVA message and SMFLIM

I was getting messages

JVMJ9VM015W Initialization error for library j9shr29(11): JVMJ9VM009E J9VMDllMain failed
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

See JVMJ9VM015W Initialization error for library j9shr29(11): JVMJ9VM009E J9VMDllMain failed

I created USER.PARMLIB(SMFLIM00) with

REGION JOBNAME(JCACHER) JOBMSG(ISSUE) MAXSHARE(80000) 

SMF logstream data sets are too small

Im getting message allocating data sets like IXGLOGR.IFASMF.VS01.DATA… every couple of minutes.

The log stream is define with

LOGSTREAM NAME(IFASMF.VS01.DATA) STRUCTNAME() LS_DATACLAS()
LS_MGMTCLAS() LS_STORCLAS() HLQ(IXGLOGR) MODEL(NO) LS_SIZE(500)
STG_MGMTCLAS() STG_STORCLAS() STG_DATACLAS() STG_SIZE(500)
LOWOFFLOAD(0) HIGHOFFLOAD(80) STG_DUPLEX(YES) DUPLEXMODE(UNCOND)
RMNAME() DESCRIPTION() RETPD(2) AUTODELETE(YES) OFFLOADRECALL(YES)
DASDONLY(YES) DIAG(NO) LOGGERDUPLEX() EHLQ(NO_EHLQ) GROUP()
MAXBUFSIZE(65532)

Where LS_SIZE(500) is 45 tracks.

I used JCL

//IBMLOG JOB 1,MSGCLASS=H                                   
//LOGDEF EXEC PGM=IXCMIAPU,REGION=4M
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DATA TYPE(LOGR) REPORT(YES)
UPDATE LOGSTREAM NAME(IFASMF.VS01.DATA)
LS_SIZE(15000)
/*

I could not run this, because the log stream was in use.

I had to create an USER.PARMLIB(SMFPRMNO) with

ACTIVE, 
DSNAME(SYS1.VS01.MAN1,
SYS1.VS01.MAN2),
RECORDING(DATASET)
DEFAULTLSNAME(IFASMF.VS01.DATA)
...

Then use

T SMF=NO

The run the command to update the logstream

and then switch back

T SMF=00

What’s next?

That’s as far as I’ve got. As I do more I’ll update this document.

I have to renew my TLS certificate every 47 days. What?!

There has been some discussion about this, and what it means. With the help of Matt Hogstrom, I now understand this a bit more.

Background

Symmetric encryption is when you change A-> B->6 etc. You use the same mapping to decrypt as you used to encrypt. If you have the mapping to encode, you can decode it. If several people use the same mapping – you can read other people’s messages.

Most digital encryption is done these days using asymmetric encryption. With an asymmetric encryption there are two keys. You encrypt with one key, and need the other key to decrypt it.

You make one key public, so anyone can have access to it, and keep the other key private and secure.

  • When you encrypt with my public key, and I decrypt it with my private key.
  • When I encrypt with my private key, you decrypt with my public key, and you know it came from me, because you used my public key.

A certificate is a package of a public key, and other information such as who it belongs to, what the certificate can be used for, and validity dates. This is packaged up, sent to a certificate authority, who digitally signs it, and returns it.

TLS is a protocol for exchanging information between a client and a server. As part of the handshake the server sends down the server’s certificate. The client checks the digital signature, and if valid, can use the enclosed public certificate to encrypt data to be sent to the server.

The problem

Your organisation, (company or country) has been using the same certificate, and so the same public key for the last 10 years. Some bad guys have been monitoring the network traffic from your organisation during this time, and saving it on tape.

Somehow the private key has escaped. Perhaps the person who looks after the plants was bribed to come in and steal the private key, or you left your laptop on the train. The bad guys have 10 years worth of data, and your private key, and can now read all the traffic sent to and from you!

This scenario has been recognised as a problem.

One solution is to change the public/private keys more frequently.

This leads to a different problem,

How often do you change the public/private key?

A year is too long. Every day is too much of an overhead. Let’s aim for one month – 30 days.

It may be that the person whose job it is to renew certificates take annual leave (up to two weeks). It would be unfortunate for a certificate to expire because that person has just gone on holiday, so let’s allow two weeks for this (Friday to Monday, 16 days). We need to give them a day to go through their mail inbox – so let’s set an expiry interval of 30 days + 16 days +1 = 47 days.

What do you renew?

In the discussion people were talking about renewing the certificate. You can renew the certificate, but specify the old public key – this defeats the purpose of the change.

You need to renew your public/private key every 47 days, and as a consequence, renew your certificate. You do not just renew the certificate with the old key.

What’s the impact?

A certificate is about to expire, so you renew it. You have a program using the old certificate. Your program needs to read the new certificate. This may mean stopping and restarting your program. For those programs which are operational all day, every day, you need to factor restarting your program. This may mean restarting it every Sunday night between 0200 and 0400. If the certificate expires during normal operation you have an emergency set of changes; renew the certificate and restart your application.

This change is for certificates used in TLS, transfering information between client and server. The certificate has a flag saying this can be used as a server, or a client (but do not specify both).

A certificate with its public key can be used for other purposes besides TLS.

For example, signing data.

Signing data

When a file is signed, you calculate the checksum (a hash value) of the file, encrypt this checksum with your private key, and attach this encrypted checksum, and your certificate to the file.

When you receive the signed file. You extract the certificate, validate it, and use the public certificate to decrypt the encrypted checksum. You do the same checksum calculation on the data and compare the two checksums. If they match the data is as the originator provided.

In this case, the public/private key does not have the same problem as TLS. The originator could be recreating the public/private key every week, it would not affect you with the package.

Intermediate certificates

You may not want every user’s certificate to be sent to the commercial certificate authorities for digital signing. You could have your own Certificate Authority.

You send a certificate to the commercial Certificate Authority, and get it signed. You then use this certificate to sign all certificates in your organisation. In this case a user’s certificate has two CA certificates attached to it. When validating a certificate the code checks that the commercial CA matches what is stored in the computer. If that matches, it uses the public key in that certificate to validate the checksum from your CA. If that matches then the public key from your CA is used to validate the end certificate.

If any CA certificate has expired, the whole certificate chain will fail validation. In this case you need your intermediate CA to be long lasting. One of the commercial CA’s has a validity period of 17+ years!

Summary

Renewing a certificate used in Client to Server (TLS) communications should have the public/private keys renewed regularly. It is recommended to have no more than 47 days.

You may want other certificates, which have longer validity periods, such as intermediate certificates.

You may want certificates where you do not care how often the public/keys are refreshed – such as code signing.

One lesson from this is you should have a different public/private key/certificate for each area; TLS, CA, and signing. The areas have different requirements.

How do you validate files on z/OS, at install time and long term?

You’ve been asked to install some files. You are not sure of their provenance, or if they have been changed from what the author wrote, and what you have received. How can you check these files?

You have some systems which are meant to be identical how can you easily check this?

One way of doing these activities is to calculate a digital hash of the file, sign the hash value, and use it.

See background for information on public/private keys.

Digitally signing is to take value, encrypt it with your private key and send it out with your certificate. To verify the signature you use the public key from the certificate to decrypt the data. It should match your copy of the value. If it matches you know it came from the certificate owner.

On z/OS you can sign load modules. These are signed at bind time, and, if configured, are checked at execution time.

Packages like openssl, and GPG (Pretty Good Privacy) have the facilities to sign objects.

This blog post covers

Installing packages

On the author’s machine

You have the Certificate Authorities file ca256.pem. This is available to every one. This could have been provided by a commercial Certificate Authority.

You have a public certificate which you will send with your package.

You have a private key matching the public certificate.

On the recipient’s machine

You have the Certificate Authority file ca256.pem.

How you do it

Create the package

You want to package data.file and distribute it. Calculate the digest of the file, and digitaly sign it

openssl dgst -sign cert.key.pem -out data.file.signature  data.file

The signature is put into data.file.signature. The signature data is in binary.

Send the following to the recipient

  • data.file
  • data.file.signature
  • your public certificate matching the private key used to sign the data. (colins.cert.pem)

Validate the package

Validate the certificate against the CA.pem 

openssl verify -CAfile ca256.pem colins.cert.pem 

If this works, you can trust the certificate.

Extract the public key from the certificate

openssl x509 -pubkey -noout -in colins.cert.pem > colins.pubkey

Validate the checksum of the file

This uses the public key extracted above

openssl dgst -verify ./colins.pubkey  -signature data.file.signature  data.file

z/OS dataset

You can use z/OS dataset, for example

openssl dgst -sign z256.key.pem -out upa.signature "//'USER.Z31B.PARMLIB(AUTORCP)'"  

where you need the double quotes and the single quotes.

Package up a package

You could create a shell script to work on a directory. For example

#!/bin/bash
for filename in ./*; do
[ -e "$filename" ] || continue
echo $filename
FILE="$(basename "${filename}")"
openssl dgst -sign z256.key.pem -out signatures/$FILE.signature $filename
done

When I ran this, the signatures directory contained the signatures of the files in the current directory.

Long term validation

You’ve validated that the files you have installed are the correct ones. Has anyone changed them since they’ve been installed?


You can issue the command to calculate the digest(hash) of a file.

openssl dgst index.txt >> dgst.txt

Which says calculate the digest – but do not sign it. The output is

SHA256(index.txt)= 1c6e0089a3ceebddf1f8e475c164162c06d7d58f29cc0b2d4c230e4e7a79cbce
SHA256(aa.txt)= d8b1fb09ac7649b61d13ca9cde72851037a83c0bca60a8545310645bb0b3da7d

You can now periodically reissue the commands, and check the value are the same as they were previously. If the values have changed – the files have changed. You can also extend this (with a small bit of Python or shell code) to include the system name

SYS1 2026/04/22 SHA256(index.txt)= 1c6e0089a3ceebddf1f8e475c...e7a79cbce
SYS2 2026/04/22 SHA256(index.txt)= d8b1fb09ac7649b61d13ca9cd...bb0b3da7d

If those files are meant to be the same on both of the systems, they clearly are not. If you run the digest command weekly you will be able to see approximately when the file was changed. It may be that maintenance was applied to one system, and the other system was overlooked.

Using code signing on z/OS

There is an IBM document Task roadmap for program signing and signature verification, this provides some of the information, and some of the information is wrong!

I’ve documented Signing load modules on z/OS.

I hit various problems. The return code from the program signing function are documented here.

Using signed modules on your system

You have been sent some modules which have been signed, and you want to user the signature verification on these modules.

One-of system wide setup

Define the system wide keyring

For the validation of signed modules there is one system wide key ring. It needs to have the signing certificates (or the CA certificates) for all of the modules being used.

RACDCERT ADDRING(VERIFY) ID(COLIN ) 

* now the profile
RDEFINE RDATALIB COLIN.VERIFY.LST UACC(NONE)
PERMIT COLIN.VERIFY.LST CLASS(RDATALIB) ID(COLIN ) ACCESS(UPDATE)
SETROPTS RACLIST(RDATALIB) REFRESH

Connect the IBM CA to the keyring

You need to connect the IBM Provided Certificate authority, to be able to verify the signature of IBM provided modules. See Listing of RACF supplied certificates.

RACDCERT ID(COLIN )CONNECT(RING(VERIFY) - 
CERTAUTH -
LABEL('STG Code Signing CA - G2')

RACDCERT LISTRING(VERIFY) ID(COLIN )

Note the lower case name.

Tell the certificate certificate facility which key ring to use.

 RDEFINE FACILITY IRR.PROGRAM.SIGNATURE.VERIFICATION  - 
APPLDATA('COLIN/VERIFY')

This ring has the certificates needed to verify.

Define the program that does the signature validation.

RDEFINE PROGRAM IRRPVERS ADDMEM('SYS1.SIEALNKE'//NOPADCHK) UACC(READ)- 
SIGVER(SIGREQUIRED(YES) FAILLOAD(ANYBAD) SIGAUDIT(ANYBAD))

Run the IRRVELD job

I had to run this when setting up for the first time. When I next IPLed, I did not need to run it. I think that RACF checks to see if the above RACF profile exists – if so – loads the code.

See here.

//IRRVERLD JOB                        
//IRRVERLD EXEC PGM=IRRVERLD

When successful, this starts and ends with return code 0 and

ICH448I RACF program signature verification function is loaded. Program signature verification is available on this system.

Define your programs that need validation

RDEFINE PROGRAM ZZZDATA  ADDMEM('COLIN.ZWIRESHA.LOAD'//NOPADCHK) - 
UACC(READ) -
SIGVER(SIGREQUIRED(YES) FAILLOAD(ANYBAD) SIGAUDIT(ANYBAD))

SETROPTS WHEN(PROGRAM) REFRESH

Where my program is called ZZZDATA and it is loaded from COLIN.ZWIRESHA.LOAD. The definition SIGVER(SIGREQUIRED(YES) FAILLOAD(ANYBAD) SIGAUDIT(ANYBAD)) says that signature is required, and if verification fails do not load the module, and write an audit record.

Each userid (or group) needs a RACF profile to specify which keyring to use to sign the programs

If you want to do IPL data signing for Validated Boot for z/OS you need the profile IRR.PROGRAM.V2.SIGNING…..

If you want to validate a general program you need the profile IRR.PROGRAM.SIGNING….

Where you can specif userid, group, or group and userid.

  • IRR.PROGRAM.SIGNING.userid
  • IRR.PROGRAM.SIGNING.goup
  • IRR.PROGRAM.SIGNING.group.user
                              
RDELETE FACILITY IRR.PROGRAM.SIGNING.COLIN
RDEFINE FACILITY IRR.PROGRAM.SIGNING.COLIN -
APPLDATA('SHA256 COLIN/SIGNING')
PERMIT IRR.PROGRAM.SIGNING.COLIN CLASS(FACILITY) -
ID(COLIN) ACCESS(CONTROL)
SETROPTS RACLIST(FACILITY) refresh

This says for userid COLIN use SHA256 and keyring COLIN/SIGNING. It will use the default key in the keyring.

You sign a module at bind time, by specifying the binder options SIGN=YES.

I added sign=yes to the binder JCL

Create a certificate to sign it

RACDCERT ID(COLIN) GENCERT -                                 
SUBJECTSDN(CN('10.1.1.2') -
O('SIGNER521') -
OU('SSS')) -
ALTNAME(IP(10.1.1.2))-
SIZE(2048) -
NOTAFTER( DATE(2026-04-22) TIME(15:22:00) ) -
KEYUSAGE(HANDSHAKE DOCSIGN) -
SIGNWITH (CERTAUTH LABEL('SIGNCA')) -
WITHLABEL('SIGNER521')

RACDCERT id(COLIN) ALTER(LABEL('SIGNER521'))TRUST

SETROPTS RACLIST(DIGTCERT) REFRESH
RACDCERT ID(COLIN )CONNECT(RING(SIGNING) -
ID(COLIN) -
DEFAULT -
LABEL('SIGNER521') )

It seems to need KEYUSAGE HANDSHAKE and DOCSIGN.

It needed the default RSA size 2048. It did not work with an ECC algorithm.

It needs to be the default in the keyring.

Execute the program

When you come to execute the signed program, the system uses the keyring defined in the

IRR.PROGRAM.SIGNATURE.VERIFICATION CLASS(FACILITY) profile. I believe this has the certificates needed to verify the programs signature.

I used keyring COLIN/VERIFY.

IEW2494E 9116 A PROBLEM WAS ENCOUNTERED WITH THE SETUP OF THE DIGITAL CERTIFICATES REQUIRED FOR PROGRAM SIGNING. RACF RETURNED SAF RETURN CODE 8 RACF RETURN CODE 8 RACF REASON CODE 00000070.

R_PgmSign 70 Ver Key ring or token does not exist or does not contain a default certificate.

Signing load modules on z/OS

There is an IBM document Task roadmap for program signing and signature verification, this provides some of the information, and some of the information is wrong!

This blog post describes how to set up your environment to sign load modules. I’ve documented Using code signing on z/OS

I hit various problems. The return code from the program signing function are documented here.

Create RACF profiles

Each userid (or group) needs a RACF profile to specify which keyring to use to sign the programs

If you want to do IPL data signing for Validated Boot for z/OS you need the profile IRR.PROGRAM.V2.SIGNING…..

If you want to validate a general program you need the profile IRR.PROGRAM.SIGNING….

Where you can specify userid, group, or group and userid.

  • IRR.PROGRAM.SIGNING.userid
  • IRR.PROGRAM.SIGNING.goup
  • IRR.PROGRAM.SIGNING.group.user
                              
RDELETE FACILITY IRR.PROGRAM.SIGNING.COLIN
RDEFINE FACILITY IRR.PROGRAM.SIGNING.COLIN -
APPLDATA('SHA256 COLIN/SIGNING')
PERMIT IRR.PROGRAM.SIGNING.COLIN CLASS(FACILITY) -
ID(COLIN) ACCESS(CONTROL)
SETROPTS RACLIST(FACILITY) refresh

This says for userid COLIN use SHA256 and keyring COLIN/SIGNING. It uses the default key in the keyring.

How to sign a load module

You sign a module at bind time, by specifying the binder options SIGN=YES.

I added SIGN=YES to the binder JCL

When it failed, there were messages like

IEW2494E 9116 A PROBLEM WAS ENCOUNTERED WITH THE SETUP OF THE DIGITAL CERTIFICATES REQUIRED FOR PROGRAM SIGNING. RACF RETURNED SAF RETURN CODE 8 RACF RETURN CODE 8 RACF REASON CODE 00000098.

These are from the R_PgmSignVer (IRRSPS00): Program Sign and Verify callable service. See the return codes. The return codes in the messages are in hex; in the documentation they are in decimal. 0x98 is decimal 152. 152 is CA or signing certificate is expired or not yet active. This was true – my signing certificate had expired.

Create a certificate to sign load modules.

RACDCERT ID(COLIN) GENCERT -                                 
SUBJECTSDN(CN('10.1.1.2') -
O('SIGNER521') -
OU('SSS')) -
ALTNAME(IP(10.1.1.2))-
SIZE(2048) -
NOTAFTER( DATE(2026-04-22) TIME(15:22:00) ) -
KEYUSAGE(HANDSHAKE DOCSIGN) -
SIGNWITH (CERTAUTH LABEL('SIGNCA')) -
WITHLABEL('SIGNER521')

RACDCERT id(COLIN) ALTER(LABEL('SIGNER521'))TRUST

SETROPTS RACLIST(DIGTCERT) REFRESH
RACDCERT ID(COLIN )CONNECT(RING(SIGNING) -
ID(COLIN) -
DEFAULT -
LABEL('SIGNER521') )

It seems to need KEYUSAGE HANDSHAKE and DOCSIGN.

It needed the default RSA size 2048. It did not work with an ECC algorithm.

It needs to be the default in the keyring.

Code signing – error messages

The messages I received were from R_PgmSignVer (IRRSPS00 or IRRSPS64): Program Sign and Verify

Return code hex 70, decimal 112

  • ICH440I Program signature error 0x10/0x00000070 for program IRRPVERS in library SYS1.SIEALNKE.
  • The program was not loaded.
  • ICH442I The digital signature appears to be valid but the root signer is not trusted.
  • ICH451I RACF encountered an error while attempting to load the program verification module. Operation code = X’06’.
  • Return code X’00000306′ and Reason code X’00000040′.
  • Supplemental diagnostic code 1 = X’00000000′.
  • Supplemental diagnostic code 2 = X’00000000′.
  • Supplemental diagnostic code 3 = X’00000000′.
  • Supplemental diagnostic code 4 = X’00000000′.
  • Supplemental diagnostic code 5 = X’00000000′.

The certificate CERTAUTH LABEL(‘STG Code Signing CA – G2’) was not trusted.

R_PgmSignVer return codes code 0x70 is 112. Key ring or token does not exist or does not contain a default certificate.

I had mis typed the certificate name.

Action:

The the name of the keyring, there is a default certificate, and the certificate has not expired.

Return code hex 64, decimal 100

At execution time I got.

ICH440I Program signature error 0x10/0x00000064 for program ZZZDATA in library COLIN.ZWIRESHA.LOAD. The program was not loaded. ICH442I The digital signature appears to be valid but the root signer is not trusted.

code 0x64 is decimal 100 which is

The program appears to be correctly signed but one of the following conditions exists:

  • The root CA certificate in the zOSSignatureInfo structure of the program object is not connected to the signature-verification key ring.
  • The root CA certificate is marked NOTRUST.

The CA of the signing code was not in the verify keyring at execution time.

Action:

Connect the certificate or its CA to the verify keyring.

Return code hex 88, decimal 136

IEW2494E 9116 A PROBLEM WAS ENCOUNTERED WITH THE SETUP OF THE DIGITAL CERTIFICATES REQUIRED FOR PROGRAM SIGNING. RACF RETURNED SAF RETURN CODE 8 RACF RETURN CODE 8 RACF REASON CODE 00000088.

The default certificate in the key ring or token does not have code signing capability.

  • When Num_parms = 10, the keyUsage extension is optional. If the extension is present, the digitalSignature bit must be set.
  • When Num_parms = 11, the keyUsage extension must be present and the digitalSignature bit must be set.

The real reason is “HANDSHAKE” or “DOCSIGN” was missing from the KEYUSAGE().

Action:

Fix the keyusage field.

Return code hex 90, decimal 144

IEW2494E 9116 A PROBLEM WAS ENCOUNTERED WITH THE SETUP OF THE DIGITAL CERTIFICATES REQUIRED FOR PROGRAM SIGNING. RACF RETURNED SAF RETURN CODE 8 RACF RETURN CODE 8 RACF REASON CODE 00000090.

The key type of one or more certificates in the key ring or token is not supported. This reason code is also issued for the following conditions:

  • When Num_parms = 11, the signing key can be stored in ICSF, but it must be a 521-bits NIST Elliptic Curve Cryptography (ECC) key. Also, the key size of any other certificates in the key ring or token must be at least 2048 bits for RSA keys, or 224 bits for NIST ECC and Brainpool ECC keys.
  • When Num_parms = 10 and an RSA CA certificate in the chain has a key size less than 2048 bits.

This was caused by using a key with

NISTECC -
SIZE(521 ) -

using the followin

RSA
SIZE(2048) -

Works.

Action:

Use RSA key with size 2048;

First steps in using system ssl

System SSL also known as GS kit provides an API for doing

  • TLS handshakes
  • Keyring manipulation
  • Encryption and signing of data

This blog post covers background information for using GSKit. I wrote it because I could not find useful samples to get me started.

Introduction

It took me some time to understand how System SSL has been written. Now I understand it, I think it is a good API. They can be more than one way of doing something, so working out what you want to to can be hard.

Use of structures

It took me a while to understand the structures. I thought they were a little complex, and started designing my own equivalent, but I then found my “improved” definition were very similar to the original ones.

For example, I want to process some application data. It has an address and a length.
With my simplistic view I would define

  • char * pBuffer;
  • int lBuffer;

You could the use these in your application.

GSKIT provides the same files in a structure.

gsk_buffer mybuffer;
mybuffer.data = "ABCD"; // data is a void * pointing to the data
mybuffer.length = 4; //

I can then do

gsk_buffer in;
gsk_buffer out;
in.data = ...
in.length = ...
gsk_rc gsk_copy_buffer(*in,*out)

For gsk_buffers that are output from a GSKIT call you need to free the contents.

gsk_free_buffer(&in);

This will release any storage which had been allocated to the structure, and set the values to 0.

Do not use this for gsk_buffers you created, because it will free the storage the data is using.

Arrays of objects

Some API functions take one certificate, and others take an array of certificates.

You define one certificate-key

 pkcs_cert_key cert_key;

For an array of certificate keys you use

// Define the array (of 2)
pkcs_cert_key cert_key[2];
// Initialise it
cert_key[0].certificate = cert;
cert_key[0].privateKey = pRecord -> privateKey;
cert_key[1].certificate = cert1;
cert_key[1].privateKey = pRecord1 -> privateKey;
// Define the structure of array of keys
pkcs_cert_keys cert_keys;
cert_keys.count = 2;
cert_keys.certKeys = &cert_key[0];

Processing arrays of objects

void print_certs(pkcs_certificates * pcs)
{
// pkcs_certificate has a count and array of certs
// iterate over them all
int i;
pkcs_certificate * pc = & pcs-> certificates[0];
for (i = 1; i <= pcs-> count;i++)
{
printf("Certificate %i\n",i);
printf("==========\n");
print_cert(pc);
pc ++;
printf(" \n");
}
}

Where print_cert() is the program I wrote which prints information about one certificate.

Useful functions

Most gskit functions return a gsk_status. You can print the interpretation of this using

if ( gskrc != 0)
printf("Error return: %s\n" gsk_strerror(gskrc));

Definitions are in

  • /usr/include/gsktypes
  • /usr/include/gskcms.h

First steps in using system ssl: compiling a program

System SSL also known as GS kit provides an API for doing

  • TLS handshakes
  • Keyring manipulation
  • Encryption and signing of data

See First steps in using system ssl gives some introductions to gskit.

The blog post provides the script I used to compile my programs. Not everything may be relevant.

name=main
export _C89_CCMODE=1
p5="-I. -I/usr/include "
p7="-Wc,DLL "
p8="-Wc,LIST(c.lst),SOURCE,XREX,SHOWINC"
p8=" " # no listing
/bin/xlc $p5 $p7 $p8 -c $name.c -o $name.o
l1="-Wl,LIST=ALL,MAP,XREF "
bind=" /usr/lib/GSKCMS31.x /usr/lib/GSKSSL.x "
/bin/xlc $name.o ${bind} -o $name -v $l1 1>bind.lst

The output from the C compile goes into c.lst, and from the bind it goes into bind.lst

The gsk includes are in /usr/include.

For the bind you need /usr/lib/GSKCMS31.x /usr/lib/GSKSSL.x

Example of system ssl to display keyring information

System SSL also known as GS kit provides an API for doing

  • TLS handshakes
  • Keyring manipulation
  • Encryption and signing of data

This blog post covers how to access a keyring and use the information in it. I wrote it because I could not find useful samples to get me started.

Introduction

It took me some time to understand how System SSL has been written. Now I understand it, I think it is a good API. They can be more than one way of doing something, so working out what you want to to can be hard.

List the certificates in a keyring

// #include <gskssl.h> // only used for TLS and SSL
#include <gskcms.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <printhex.h>
int main(int argc, char * argv[]) {
int rc;
gsk_status gskrc;
gsk_handle hRing ;
int count = 0;// the number of certs is put here
char * ring = "START1/TN3270";
printf("Open keyring %s\n",ring);
gskrc= gsk_open_keyring (ring,
&hRing,
&count );
if (gskrc != 0)
{
printf("gsk_open_keyring %s\n", gsk_strerror(gskrc));
return 8;
}
printf("Number of certs in the ring %i\n",count);
//
// Get the default label (if it exists)
//
char * default_label;
gskrc = gsk_get_default_label(hRing,&default_label);
if (gskrc != 0)
{
else printf("gsk_get_default_label error %s\n",gsk_strerror(gskrc));
return 8;
}
printf("gsk_get_default_label %s\n",default_label);
//
// now get the record with this label (or by number)
//
gskdb_record * pRecord = 0;
gskrc = gsk_get_record_by_label(hRing,default_label,&pRecord);
//gskrc = gsk_get_record_by_id(hRing,4,&pRecord); // by number
if (gskrc != 0)
{
printf("gsk_get_record_by_label worked %s\n",gsk_strerror(gskrc));
return 8;
}
// The value is a half work enum
printf("Record Type %hi\n",pRecord ->recordType);
#include <readLabels.h>
#include <listall.>
gskrc = gsk_close_database(&hRing);
return 0 ;
}

ReadLabels

With gsk_get_record_labels you can request those with private keys (1) or those without private keys(0)

If you want all certificates on the ring use without private_keys(0).

gskrc = gsk_get_default_label ( hRing,&default_label);
if (gskrc == 0)
printf("gsk_get_default_label %s\n",default_label);
else printf("gsk_get_default_label error %s\n",gsk_strerror(gskrc));
char ** pl = 0;
int countL = 0;int whichRecords = 0;
printf("/nGet records with no private key (%i)\n",whichRecords);
gskrc = gsk_get_record_labels (hRing,
whichRecords, // those with public keys only
// 1 for this with private key
&countL, // How many returned
&pl); // char *** labels
if ( gskrc != 0)
{
printf("gsk_get_record_labels%s\n",gsk_strerror(gskrc));
return 8
}
for (int j = 0; j<countL ;j++)
{
printf("Label %i,%s\n",j,*pl);
pl++;
}
printf("============\n");

It took me some time to get the list of labels printed out. The value is a char ***;

  • If you had char * p; p is a pointer to a string so is a char *.
  • char * q[10]; pl = &q[0]
  • pl is a pointer to the elements in the array. pl is addr(a char * ) so char **;
  • the address of pl is passed to the function, so it is addr( a char **) so is a char ***
  • In the loop we get what pl points to (*pl). This is a pointer to a string. so we have printf(“…”,*pl);

Listall

printf("Number of records %i\n",count);
for (int j = 1 ; j<count+1 ;j++) // starts at 1
{
gskrc = gsk_get_record_by_index(hRing,j,&pRecord);
printf("Record %i:n",j);
printRecord(pRecord);
}
printf("============\n");

printRecord

void printRecord( gskdb_record * pRecord)
{
printf("Record Type %hi\n",pRecord ->recordType);
printf("Record Flags %2.2x\n",pRecord ->recordFlags);
printf("Record ID %i\n",pRecord ->recordId);
printf("Record Issuer ID %i\n",pRecord ->issuerRecordId );
printf("Record Label %s\n",pRecord ->recordLabel);
printf("============\n");
}

Output

The program printed out

Open keyring START1/TN3270
Number of certs in the ring 9
gsk_get_default_label NISTEC256
gsk_get_record_by_label worked
precrd 2 20a2e6e0
Sizeof gskdb_record_type 2
gsk_get_default_label NISTEC256

Get records with no private key (0)

Label 0,DOCZOSCA
Label 1,JUN24CA
Label 2,DocZosCADSA
Label 3,CA256

============
Number of records 9
Record 1:
Record Type 2
Record Flags 80
Record ID 1
Record Issuer ID 1
Record Label DOCZOSCA
...

Record 9:
Record Type 3
Record Flags c0
Record ID 9
Record Issuer ID 1
Record Label NISTEC256
============

  • The value of Record 1 type gskdb_rectype_certificate = 2, /* Certificate */
  • Flags is
    • GSKDB_RECFLAG_TRUSTED = The certificate is trusted ( 0x80);
    • GSKDB_RECFLAG_DEFAULT = This is the default key
  • ID 1 – record 1
  • Issuer ID 1 – it is self signed
  • Label DOCZOSCA
Record 9:
Record Type 3
Record Flags c0
Record ID 9
Record Issuer ID 1
Record Label NISTEC256

  • The record type is 3 gskdb_rectype_certKey = 3, /* Certificate with key */
  • Flags is
    • GSKDB_RECFLAG_TRUSTED = The certificate is trusted.
    • GSKDB_RECFLAG_DEFAULT = This is the default key
  • ID 9 – it is record 9 in the keyring
  • Issuer ID 1- it was signed by the certificate with position 1 (DOCZOSCA)
  • Its label is NISTEC256

Example of using system ssl to print certificate information

The structure containing information about a certificate has a very deep nesting of structures and arrays. Fortunately for the most common information, gskit provides an interface.

There is a standards document giving the layout of the certificate, and what the fields mean.

The structures are in /usr/include/gskcms.h. Some of the data types are in /usr/include/gsktypes.h .

The certificate structure

  • The structure pkcs_certificate>s has an array of pkcs_certificate and a count of the elements.
  • The structure pkcs_certificate has a certificate type and an x509_certificate.
  • The structure x509_certificate has
    • a tbsCertificate
    • signature algorithm
  • The x509_tbs_certificate structure (tbs from To Be Signed) has
    • the certificate version
    • the certificate serial number
    • the signature algorithm of the certificate
    • the issuer
    • the validity period
    • the subject.

The fields

  • The certificate serial number. This a hexadecimal value. The definition has a pointer to the data, and a length of the data. The length is at least one.
  • The signature algorithm of the certificate. This includes
    • The signature type. This is a C enum, such as x509_alg_sha256WithRsaEncryption = 25
  • The issuer is a complex nest of structure. Fortunately gskit provide a function gsk_name_to_dn(structure,name). The output “name” is a printable EBCDIC string like CN=DocZosCA,OU=CA,O=COLIN
  • The validity period. This structure contains two time-value fields, not before, and not after.
    • You can format a time value field using
      • char dest[18];
      • strftime(dest,18,”%y/%m/%d %H:%M:%S”,timeval);
  • The subject is like the issuer. Use the function gsk_name_to_dn to convert it to printable EBCDIC. For example CN=10.1.1.2,OU=SSS,O=NISTECC521

Printing the structures

I have some functions like

void print_certs(pkcs_certificates * pcs)
{
// pkcs_certificate has a count and array of certs
// iterate over them all
int i;
pkcs_certificate * pc = & pcs-> certificates[0];
for (i = 1; i <= pcs-> count;i++)
{
printf("Certificate %i\n",i);
printf("==========\n");
print_cert(pc);
pc ++;
printf(" \n");
}
}

which invokes

void print_cert(pkcs_certificate * pc )
{
print_x509_algorithm_type(pc ->u.certificate. signatureAlgorithm
.type,0);
// Serial number
char output[60]; // into here
// hexof is written by Colin Paice
printf("Serial number Hex :%s\n",hexof(
pc ->u.certificate.tbsCertificate.serialNumber.data,
pc ->u.certificate.tbsCertificate.serialNumber.length,
&output[0]) );
// get the subject and issuer names
gsk_status gskrc;
char * pName; // returned here - must free it
pName = "Unknown";
gskrc = gsk_name_to_dn(
& pc ->u.certificate.tbsCertificate.subject,
&pName);
if (gskrc != 0)
printf("Subject gsk_name_to_dn %s\n", gsk_strerror(gskrc));
printf("Subject name :%s\n",pName);
gsk_free_string(pName);
// extract the issuer,
pName = "Unknown";
gskrc = gsk_name_to_dn (
& pc ->u.certificate.tbsCertificate.issuer,
&pName);
if (gskrc != 0)
printf("Issuer gsk_name_to_dn %s\n, gsk_strerror(gskrc));
printf("Issuers name :%s\n",pName);
gsk_free_string(pName);
// extract the valid times.
char buffer[18];
printf("Not Before :%s\n",
ccpformattime (
& pc ->u.certificate.tbsCertificate.validity.notBefore,
&buffer[0] ) );
printf("Not After :%s\n",
ccpformattime (
& pc ->u.certificate.tbsCertificate.validity.notAfter,
&buffer[0] ));
print_x509_extensions(&pc ->
u.certificate.tbsCertificate.extensions);
}

Process DER ASN.1

Some data is encoded in ASN.1 format.

This is of format

  • 1 byte of type
  • 1 or more bytes of length
  • Value, where value can be
    • Integer data
    • ASCII string data
    • A sequence of things ..

For example

16304765 6E657261… is

  • Type 16 is an ASCII string
  • of length 0x30
  • with value 0x4765… which translates to EBCDIC Genera…

30068704 0A010102

  • Type 30 is a sequence (it happens to be a sequence of one element)
  • Of length 6
    • The value is a dotted decimal IP address of length 4
    • with value 0A010102 – which is 10.1.1.2.
Certificate 2
==========
Certificate algorithm :x509_alg_sha256WithRsaEncryption
Serial number Hex :68
Subject name :CN=10.1.1.2,OU=SSS,O=NISTEC256
Issuers name :CN=DocZosCA,OU=CA,O=COLIN
Not Before :26/04/05 00:00:00
Not After :27/04/05 23:59:59
Extension 1
============
Extension type:x509_ext_unknown
DER IA5String: Generated by the Security Server for z/OS (RACF)

Extension 2
============
Extension type:x509_ext_subjectAltName
DER Dotted address: 0x0A010102

Extension 3
============
Extension type:x509_ext_keyUsage
DER BitString: 0x0780
Sample output

Extension 4
============
Extension type:x509_ext_subjectKeyIdentifier
DER Octet: 0xF421DD55 F00BEE6E E81B97D2 1D5556AB A671EC59

Extension 5
============
Extension type:x509_ext_authorityKeyIdentifier
DER Context: 0xCAD12CE6 AACB598C 6E9595CD D812C90A C9C4C289