How hard can it be to copy a directory on z/OS

My home directory had filled up, and I wanted to copy my files to a file system with more space. This took me much longer than I expected.

Philippe Richard said you should take a look at copytree (in /samples). Nice tool to clone/copy directories. can be run as a unix script or under TSO REXX.

copy using cp

I used cp -R olddir newdir but not all of the files were copied.

tar complained

I used tar -cf old /u/colin/* but got

FSUM7218 tar: /u/colin/env/lib/python3.12/….pyc: name too long

I got pax wrong (1)

I used

pax -w – -f /u/tmp/zowet/archive /u/colin/*

The gave message

FSUMF319 File tag exists but pax cannot store the tag information using this format/option(s) for file /u/colin/env/lib…json.py

I needed the -x os390 option

I got pax wrong (2)

pax -w -x os390 -f /u/tmp/zowet/archive /u/colin/*

The backup worked fine. Unfortunately when I restored from the archive it overwrote the same directory – because I had explicitly coded the fully qualified name /u/colin.

I got pax wrong (3)

cd /u/colin
pax -w -x os390 -f /u/tmp/zowet/archive .

The files are relative to the directory (.) when the files are restored they are also relative to the directory

cd /u/tmp/zowet/colin
pax -ppx -rvf /u/tmp/zowet/archive

This command unpaxes the files from /u/tmp/zowet/archive into the current directory, and prints out the names (-v) as it does so.

The names were displayed, like

./bsdel.sh

so you can see the file name is a relative, rather than fully qualified.

It looked like it worked – but the files belonged to the userid that created them from the pax file.

I got pax right

pax -ppox -rvf ../archive

The -p option specifies which file characteristics to restore

  • o Preserves the user ID and group ID.
  • p Preserves the file mode: access permissions (without modification by umask), set-user-ID bit, set-group-ID bit, and sticky bit.
  • x Preserves extended attributes

I’m sure there are other methods to do the copy.

How to get out to the internet. SNAT, DNAT and MASQUERADE

This follows on from concepts explained in If we all have the same IP addresses how does the internet work?

The high level problems

I have a Linux server machine connected to my laptop via Ethernet. How can I change the destination of where data flows?

I want to be able to say

  • Any traffic coming in over Ethernet for IP address 98.76.54.32, route it to the server on my other laptop with address 10.0.0.6. This is changing the Destination Address of a packet., or DNAT: (changing the) Destination Network Address Table to a specific address.
  • I want to be able to send stuff sent over Ethernet with an internal IP address, and route it to external servers. In effect I want to make the data from my server machine be sent to the internet, with the IP address of my laptop. This is changing the Source address of a packet, or SNAT: (changing the) Source Network Address Table to a specific address.

The home address of my z/OS system was 10.1.1.2. My Linux machine has IP address 192.168.1.139.

Kindergarden concepts of a router

Traffic comes in to a router. There are rules which control how traffic is routed, for example this address range should go down the Ethernet connection, anything else (the default) goes over the wireless connection.

Below the surface

I picture the router as 3 boxes in a row. Before – router – after.

  • Before: You can specify rules to be applied before the data gets to the routing code. This allows you to change information in the packet header, such as destination, or port address. The rule type for this are called PREROUTING. The packet then flows into…
  • The router: This decides where each packet goes. The packet the flows into…
  • After: You can change the packets before it gets send down the interface. This rule type is POSTROUTING.

Changing the destination

The command on my Linux laptop

iptables -t nat -A PREROUTING -p tcp --dport 1122 -j DNAT --to-destination 10.0.0.6:3344

Send all TCP traffic destined for port 1122 to the machine with IP address 10.0.0.6, and change the port to 3344.

It is PREROUTING, meaning that make the change before any routing decisions are made.

Changing the source – getting the data to the outside world

The following command on my Linux laptop

iface=wlxcc641aee92c5
sudo iptables -t nat -A POSTROUTING -s 10.1.1.2 -o $iface -j SNAT --to-source 192.168.1.139

tells Linux to take any traffic from 10.1.1.2, send it over the interface wlxcc641aee92c5 and change the Source Network Address Translation (SNAT) so it looks like it came from 192.168.1.139 ( my wireless interface).

This is POSTROUTING because the routing decision has already been made, and the data is ready to be sent over the interface(eg wireless).

This is fine as long as you know the IP address of your interface (192.168.1.139). If your router has DHCP, the Linux may get a different address every time. In this case you can use

iface=wlxcc641aee92c5
sudo iptables -t nat -A POSTROUTING -s 10.1.1.2 -o wlxcc641aee92c5 -j MASQUERADE

which I believe says for the specified source address 10.1.1.2 and use the address of the -o output device.

You might just use the MASQUERADE option every time as it is easier to type.

If you want to specify all traffic (or just want it to work) you can omit the -s

iface=wlxcc641aee92c5
sudo iptables -t nat -A POSTROUTING -o $iface -j MASQUERADE

There are some good examples here.

Problems getting out of z/OS to the outside world, unknown host

I was in OMVS trying to install some software, but it could not find and use the IP address of the server. My current /etc/hosts file is here.

What’s the problem?

I issued

ping pypi.org      

and got

EZZ3111I Unknown host 'pypi.org'                     

There are two solutions

Capture an (IP address) resolver trace

I issued the command

export RESOLVER_TRACE=~/trace 

and reran the command.
This gave

 Resolver Trace Initialization Complete -> 2025/11/26 10:53:47.591994 

res_init Resolver values:
Setup file warning messages = No
CTRACE TRACERES option = No
Global Tcp/Ip Dataset = ADCD.Z31B.TCPPARMS(GBLTDATA)
Default Tcp/Ip Dataset = ADCD.Z31B.TCPPARMS(GBLTDATA)
Local Tcp/Ip Dataset = /etc/resolv.conf
Translation Table = TCPIP.STANDARD.TCPXLBIN
UserId/JobName = COLIN
...
res_init Succeeded
res_init Started: 2025/11/26 10:53:47.650509
res_init Ended: 2025/11/26 10:53:47.650537
***************************************************************************
GetAddrInfo Started: 2025/11/26 10:53:47.650677
GetAddrinfo Invoked with following inputs:
Host Name: pypi.org
No Service operand specified
Hints parameter supplied with settings:
ai_family = 0, ai_flags = 0x00000062
ai_protocol = 0, ai_socktype = 0
No NameServers specified, no DNS activity
GetAddrInfo Opening Socket for IOCTLs
BPX1SOC: RetVal = 0, RC = 0, Reason = 0x00000000, Type=IPv4
BPX1IOC: RetVal = 0, RC = 0, Reason = 0x00000000
GetAddrInfo Opened Socket 0x00000004
GetAddrInfo Only IPv4 Interfaces Exist
GetAddrInfo Searching Local Tables for IPv4 Address
Global IpNodes Dataset = ADCD.Z31B.TCPPARMS(ZPDTIPN1)
Default IpNodes Dataset = ADCD.Z31B.TCPPARMS(ZPDTIPN1)
Search order = CommonSearch
SITETABLE from globalipnodes ADCD.Z31B.TCPPARMS(ZPDTIPN1)
- Lookup for pypi.org
GetAddrInfo Closing IOCTL Socket 0x00000004
BPX1CLO: RetVal = 0, RC = 0, Reason = 0x00000000
GetAddrInfo Failed: RetVal = -1, RC = 1, Reason = 0x78AE1004
GetAddrInfo Ended: 2025/11/26 10:53:47.664992

Where 0x78AE1004 is The GETADDRINFO call failed because the host name cannot be found in DNS, or in the z/OS host configuration files (/etc/hosts or hlq.HOSTS.ADDRINFO).

I think this message is not very helpful. It was not found in sitetable

Default IpNodes Dataset = ADCD.Z31B.TCPPARMS(ZPDTIPN1) 

Use a Dynamic Name Server

You can tell TCPIP to go to a Name Server to look up the address in the internet.

The JCL for my RESOLVER started task has

//* 
//* 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=*
//*

The //SETUP member GBLRESOL has

  DEFAULTTCPIPDATA('COLIN.TCPPARMS(GBLTDATA)') 
GLOBALTCPIPDATA(/etc/resolv.conf)
;
...

File /etc/resolv.conf has

nameserver 8.8.8.8 
nameserver 1.1.1.1

I enabled a resolver trace and pinged a new website.

The trace includes

**************************************************************************
GetAddrInfo Started: 2025/12/16 17:00:54.153989
GetAddrinfo Invoked with following inputs:
Host Name: hmrc.co.uk
...
res_search(hmrc.co.uk, C_IN, T_A)
res_search Host Alias Search found no alias
res_querydomain(hmrc.co.uk., , C_IN, T_A)
res_querydomain resolving name: hmrc.co.uk.
res_query(hmrc.co.uk., C_IN, T_A)
Querying resolver cache for hmrc.co.uk.
EZBRECFR: RetVal = 0, RC = 0, Reason = 0x00000000
No cache information was available
...
* * * * * Beginning of Message * * * * *
...
Number of Question RRs: 1
Question 1:
hmrc.co.uk
...
* * * * * End of Message * * * * *
res_send Name Server Capabilities
Monitoring intervals used = 5
Name server 8.8.8.8
...
Name server 1.1.1.1
...
res_send Sending query to Name Server 8.8.8.8
...
res_send received data via UDP. Message received:
* * * * * Beginning of Message * * * * *
...
Question 1:
hmrc.co.uk
...
Number of Answer RRs: 1
Answer 1:
hmrc.co.uk
...
TTL: 3600 (0 days, 1 hours, 0 minutes, 0 seconds)
195.171.114.178
* * * * * End of Message * * * * *
...
Attempting to cache results for hmrc.co.uk.
EZBRECAR: RetVal = 0, RC = 0, Reason = 0x00000000
Cache information was saved

The TTL from the DNS server says

 TTL:  3600 (0 days, 1 hours, 0 minutes, 0 seconds) 

When there was another request to the site within this time, the trace had

GetAddrInfo Started: 2025/12/16 17:01:09.426161 
GetAddrinfo Invoked with following inputs:
Host Name: hmrc.co.uk
...
Querying resolver cache for hmrc.co.uk.
EZBRECFR: RetVal = 0, RC = 0, Reason = 0x00000000
Cache data from 8.8.8.8 was retrieved
...
GetAddrInfo Succeeded: IP Address(es) found:
IP Address(1) is 195.171.114.178

showing the value from the DNS was retrieved from the local cache.

Explicitly specify the name to IP address mapping

If you do not want to use the DNS you can specify your own name to IP address mapping.

I reconfigured my RESOLVER started task to have

Global IpNodes Dataset  = /etc/hosts 
Default IpNodes Dataset = COLIN.TCPPARMS(ZPDTIPN1) 

and edited /etc/hosts to include

#IPAddress             Hostname   alias 
151.101.128.223        pypi.org    pip 

and the ping pypi.org worked

The trace file now had

 Global IpNodes Dataset  = /etc/hosts 
Default IpNodes Dataset = COLIN.TCPPARMS(ZPDTIPN1)
Search order = CommonSearch
SITETABLE from globalipnodes /etc/hosts
- Lookup for pypi.org
ADDRTABLE from globalipnodes /etc/hosts
- Lookup for 151.101.128.223
GetAddrInfo Returning Zero as Port Number
GetAddrInfo Built 1 Addrinfos
GetAddrInfo Closing IOCTL Socket 0x00000004
BPX1CLO: RetVal = 0, RC = 0, Reason = 0x00000000
GetAddrInfo Succeeded: IP Address(es) found:

When I changed this file to have multiple examples for the pypi.org

#IPAddress             Hostname   alias 
151.101.128.223 pypi.org pip
151.101.192.223 pypi.org pip

the trace file had

GetAddrinfo Invoked with following inputs: 
Host Name: pypi.org
...
SITETABLE from globalipnodes /etc/hosts
- Lookup for pypi.org
ADDRTABLE from globalipnodes /etc/hosts
- Lookup for 151.101.128.223
GetAddrInfo Returning Zero as Port Number
GetAddrInfo Built 2 Addrinfos
GetAddrInfo Closing IOCTL Socket 0x00000004
BPX1CLO: RetVal = 0, RC = 0, Reason = 0x00000000
GetAddrInfo Succeeded: IP Address(es) found:
IP Address(1) is 151.101.128.223
IP Address(2) is 151.101.192.223

and it returned both of them to the caller

My current /etc/hosts

After doing some Pip work to install products, my /etc/hosts file is now

#IPAddress             Hostname   alias 
151.101.128.223        pypi.org    pip 
151.101.192.223        pypi.org    pip 
151.101.192.223        files.pythonhosted.org   pipfiles 
20.26.156.215          github.com 
151.101.128.81         bbc.co.uk 
151.101.1.91           curl.se 
185.199.110.133        raw.githubusercontent.com 
185.199.110.133        release-assets.githubusercontent.com 
169.63.188.167         downloads.pyaitoolkit.ibm.net 

I created this list by resolver_trace to find the hostnames which failed, then adding them to the file along with their addresses using nslookup name.

Why does one ping work, and the same ping doesn’t?

I was trying to check connectivity from z/OS running on my laptop. For some remote sites I could issue ping and get a response back. For some other sites I issue the ping and did not get a response back.

When I issued the pings from Linux – they both worked.

I noticed that for the pings from z/OS the field Timestamp from icmp data (relative): was 27 seconds behind. This was caused by z/OS adding leap seconds. It was not the problem.

By comparing all the fields in a successful and an unsuccessful ping, I could see that z/OS send 256 bytes of data, and Linux sent only 40 bytes of data.

On Linux, when I used

ping …. -s 256

it failed. When I used

ping …. -s 20

it worked.

Similarly on z/OS.

ping .... (length 20

The defaults lengths are different between z/OS and Linux.

The moral of this tale is

If ping does not return any data – try a very short ping.

If we all have the same IP addresses how does the internet work?

At home my IP address is 192.168.1.139. I went to a local cafe, and my IP address was the same. If use the internet, how does the server now which 192.168.1.139 to send the data to. (I went to the town hall and got 192.168.1.25 so it is not always the same address).

I thought it was a bit like gravity – it just works. But in gravity’s case, no one knows how it works.

With the internet, it is easy – until it is not easy. It is called Network Address Translation or NAT.

How does it work?

I access the internet through a BT Smart hub. It has an IP address on the internet of 87.65.43.21. For the moment assume this address is unique in the internet.

The IP address of my laptop is 192.168.1.139, and is unique within my home hub area. My old laptop has a different IP address on my home network.

When my laptop connects to a server, such as google, BBC etc, the browser opens a port (for example 99) to the local TCPIP, and the request goes to the hub over the wireless interface.

The hub picks a free port (123) for this session, builds an internal table of my laptop’s IP address 192.168.1.139 + port 99, and the hub’s port 123. The hub then sends the request to the destination – with the “originator” address set to 87.65.43.21 port 123. The server responds with data for 87.65.43.21 port 123. My home hub then looks in its table for port 123 and says this maps to 192.168.1.139 port 99, and sends it down to my laptop.

That’s all pretty easy. I mentioned that the address 87.65.43.21 is unique in the internet. That statement is not strictly true. It is unique in the BT network for Orkney and north Scotland. In another part of the country – such as Wales, there will also be a hub with address 87.65.43.21. So how does this work….. ? Easy, it is the same as before

Somewhere in north Scotland BT has a big router. This might only support IPV6, and this router has IP address 2000:1234:5678::99

When a request for a new connection comes in from 87.65.43.21 port 123 the big BT router builds an internal table of 87.65.43.21 port 123 mapping to 2000:1234:5678::99 port 222. This address gets send onwards to the server. The server gets the request with originator 2000:1234:5678::99 port 222, does some work, and sends the response back to the big router in Scotland. The big BT router looks up port 222, finds it is for address 87.65.43.21 port 123, and sends it down to my home hub.
My home hub gets the request looks in its internal tables and sends it on to my laptop.

There will be a big router in Wales with it’s own IP address, so the 87:65:43:21 in Wales will have a different IP address to mine, when its requests get to the server.

This way every one can have the same IP address and we all get connected to the internet.

What does a Wireshark trace look like?

I was running z/OS on zD&T on Linux.

  • The IP address of z/OS had home 10.1.1.2
  • The IP address of Linux was 192.168.1.139
  • I used Wireshark on the Wireless interface.
    • A ping from TSO on z/OS showed up as being from 192.168.1.139 – the Linx address
    • The response came back to 192.168.1.139

Is it that simple?

No. This is where I’ve made some guesses because I could not find any more information.

  • A ping from z/OS with host address 10.1.1.2 went out with source IP address of 192.168.1.139.
  • A ping from Linux went out out with source IP address of 192.168.1.139.

I think that the mapping of IP address is a little more complex that I first described.
The Linux box needs to know which requests came from z/OS and so the response needs to be sent to z/OS and which request came locally. Some TCP packets have a sequence and identifier, it may be that these are used to keep track of individual packets, and so Linux can route them.

But…

I said at the top With the internet, it is easy – until it is not easy. The route a request takes to a server can be different to the route the response takes from a server. I do not understand how this works if NAT is used. Perhaps you always have to go through “the big routers” doing NAT, but the path from my laptop to the “big routers” can vary, going through routers which do not do NAT.

My mental picture is “Hub Airport”

  • I can take any route to get to the airport from my house.
  • At the airport, I can take any airline to get to my destination, either directly or via hops.
  • At the remote airport I can take any route to drive to my hotel.

The airports are routers doing the NAT.

How do I configure my new laptop to run my work?

This is an aid to help me when I had a new laptop, and the things I had to do. Some people may find some of the things I use, useful in their work. Because I made some big mistakes, it was worth writing things down.

If people have other good Ubuntu tools which they consider essential, please let me know


Note: It is a list of things to do – not a set of instructions.

Windows

  • mangage bitlocker – disable so you can resize the partition
  • set fast boot off so you can resize the partition

Lenovo bios change

  • F1-F12 as primary function -> On. Without this ISPF P9 gives print screen.
  • Enable trackpoint
  • Disable trackpad
  • Swap Ctrl FN

Prepare the laptop

  • Create Linux bootable image for the correct architecture
  • Boot the Linux USB
  • Change partitions, shrink Windows allocate Linux partition, allocate user partition
  • Install Linux
  • Check Windows still starts

Boot the installed Linux

  • Connect to Wifi
  • Sudo apt update
  • Sudo apt install ssh
  • Sudo apt install sshfs
  • Sudo snap install vivaldi
  • Use disks to create a user parition on the SSD.
    • Format it
    • Edit mount options
    • Unselect User session defaults
    • Display Name: colins
    • Mount point /mnt/Colin
    • Identify as LABEL=Colin
    • Reboot and check disk is mounted
  • Add backup userid
    • sudo adduser ColinPaice
    • sudo passwd ColinPaice
    • sudo adduser ColinPaice sudo
    • sudo mkdir /home/ColinPaice
    • sudo chwon ColinPaice:users /home/ColinPaice
    • su – ColinPaice Check it works
  • Change userid to its parition on /mnt/Colin
    • logon as the backup userid ColinPaice
    • usermod --home /mnt/colin colin
    • su – colin to check it works
  • sudo apt install x3270 do this before installing openssh-server because of font problems
  • sudo apt install openssl-server
  • sudo apt install traceroute
  • set up sshd
    • sudo ufw allow ssh
    • sudo ufw enable
    • sudo systemctl enable ssh
    • sudo systemctl start ssh
    • sudo systemctl status ssh
  • use system to change Ethernet network to
    • Manual 10.1.0.4 255.255.255.0
  • From old laptop ssh colin@10.1.0.4
  • sudo snap install discord
  • sudo apt install wmctrl so I can use hot keys to switch ispf windows
  • Set up networking
    • sudo ip -4 addr add 10.1.0.2/24 dev enp1s0f0
  • save/restore keyboard mappings
    • dconf dump /org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/ > custom.txt
    • cat custom.txt | dconf load /org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/
    • all: dconf dump / > dconf-backup.conf
    • all: cat dconf-backup.conf | dconf load /
  • Move window buttons to the top left sudo apt install gnome-tweaks; gnome-tweaks; windows-: Placement left
  • Set dash icon size. Settings-> Ubuntu desktop -> Dock -> Icon size
  • Add my hot directories to the Gnome file manager side bar.
    • Display the directory and its contents, click on ⋮ select “add to bookmarks”.
  • sudo apt install dconf-editor
  • sudo apt install gnome-shell-extension-manager
    • super – extension manager browse clipboard-history – install
      • Shift super sudo apt install gnome-screenshot
  • For backups sudo apt install deja-dup duplicity
  • Setting icon size for file manager.
    • dconf /org/gnome/nautilus/list-view
    • use default value off
    • custom value ‘small’

Setting up applications to use z/OS on a remote Linux machines

  • For hot key to ISPF sessions sudo apt install wmctrl . system -> keyboard -> keyboard short cuts -> custom
    • Name: colin@ wmctrl -a colin@ shortcut ctrl + 3
    • Name: mst cons wmctrl -a mstcon shortcut ctrl + 1
    • Name: tso@ wmctrl -a tso@ shortcut ctrl + 2
  • sudo apt install wireshark
  • sudo apt install curl

Why can’t I change the colour of my Gnome terminals on my new Linux image?

I’ve got a new laptop, and I’ve spent quite a lot of time migrating stuff from my old one.

It all seemed to be working, except I could not change the colour of my Gnome terminals.

The documentation (and many comments on the internet) say hamburge(≡) -> Profile -> pick your favourite colour from the options. Unfortunately I did not have “Profile” as an option.

Gnome configuration information is stored in a directory tree format under /org/gnome/terminal/legacy/profiles:

This can be see by using:

  • dconf-editor is a GUI tool to allow direct editing of the dconf configuration database.
  • dconf is a simple tool for manipulating a dconf database.
  • gsettings offers a simple command line interface to GSettings. It lets you get, set or monitor an individual key for changes.

From these I could see that for the userid with problems (another userid was OK) I was missing some configuration information.

On my old system I exported the part of the configuration tree using the command

dconf dump /org/gnome/terminal/legacy/profiles:/ > dconf.dump

I sent this file across to my new system, backed up .config/dconf/user and used

dconf load /org/gnome/terminal/legacy/profiles:/ < dconf.dump

I immediately had access to the hamburger icon; it had “Profile >”; and I could pick a colour.

What is in the dumped configuration file?

The dconf.dump file user above contained

[/]
default='f8084ff0-88c6-43ad-b674-d901f5f818a5'
list=['b1dcc9dd-5262-4d8d-a863-c897e6d979b9', '2e126889-4012-485a-a363-057135d6b038', 'f8084ff0-88c6-43ad-b674-d901f5f818a5', '990b09a4-8a20-4ba5-aab6-ad88fdc531dd', 'd963eefc-9bf1-4ae1-8653-e8d36bd6127a', '110cf44a-82fd-454c-bc12-b3918b987cde']

[:110cf44a-82fd-454c-bc12-b3918b987cde]
background-color='rgb(56,111,67)'
use-theme-colors=false
visible-name='ddd'

[:2e126889-4012-485a-a363-057135d6b038]
background-color='rgb(238,238,236)'
foreground-color='rgb(46,52,54)'
use-theme-colors=false
visible-name='blue'
...
[:f8084ff0-88c6-43ad-b674-d901f5f818a5]
default-size-columns=100
use-theme-colors=true
visible-name='default'

There is a definition (at the bottom)

  • id :[:f8084ff0-88c6-43ad-b674-d901f5f818a5]
  • default-size-columns=100
  • use-theme-colors=true
  • visible-name=’default’

I refer to this as default (the visible name) using the system theme colours.

There is a definition called ‘blue’.

  • use-theme-colors=false says do not use the standard theme colour; the colours are overridden
  • the background colours are specified
  • the foreground colours are specified
  • it has an id of 2e126889-4012-485a-a363-057135d6b038

At the top of the file is

  • default=’f8084ff0-88c6-43ad-b674-d901f5f818a5′ this refers to the section which I’ve called default.
  • the list of possible values includes those for default and blue.

Creating a ZFS – which way should I do it? IDCAMS LINEAR or IDCAMS ZFS?

When I looked into creating a ZFS (so I could run use it in the Unix environment) I found there were two ways of doing it both have the same end result.

The “old” way – a three step process

You use DEFINE CLUSTER …LINEAR to create the data set, then use PGM=IOEAGFMT to format it, then mount it.

//IBMUZFS  JOB ,' ',COND=(4,LE) RESTART=MOUNT 
//DEFINE EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DELETE COLIN.ZOPEN.ZFS CLUSTER
SET MAXCC=0
DEFINE -
CLUSTER -
(NAME(COLIN.ZOPEN.ZFS)-
LINEAR -
VOLUMES(USER10 ) -
STORCLAS(SGBASE ) -
MEGABYTES(6000 1000) -
SHAREOPTIONS(3 3))
/* -
//FORMATFS EXEC PGM=IOEAGFMT,REGION=0M,COND=(0,NE,DEFINE),
// PARM=('-aggregate COLIN.ZOPEN.ZFS ')
//* PARM=('-aggregate COLIN.ZOPEN.ZFS -compat')
//SYSPRINT DD SYSOUT=*
//STDOUT DD SYSOUT=*
//STDERR DD SYSOUT=*
//*
//*
//MOUNT EXEC PGM=IKJEFT1A,COND=((0,NE,DEFINE),(0,NE,FORMATFS))
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
MOUNT FILESYSTEM('COLIN.ZOPEN.ZFS') TYPE(ZFS) +
MOUNTPOINT('/u/zopen') +
MODE(RDWR) PARM('AGGRGROW') AUTOMOVE
/*

The define took less than a second, the format took about 16 seconds, and the mount took less than one second

The Two step (sounds like a dance for system administrators)

You create the dataset with type ZFS, you then mount it, and the mount formats it.

//IBMUZFS  JOB ,' ',COND=(4,LE) RESTART=MOUNT 
//DEFINE EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DELETE COLIN.ZOPEN.ZFS CLUSTER
SET MAXCC=0
DEFINE -
CLUSTER -
(NAME(COLIN.ZOPEN.ZFS)-
ZFS -
VOLUMES(USER10 ) -
STORCLAS(SGBASE ) -
MEGABYTES(6000 1000) -
SHAREOPTIONS(3 3))
/*
//MOUNT EXEC PGM=IKJEFT1A,COND=((0,NE,DEFINE))
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
MOUNT FILESYSTEM('COLIN.ZOPEN.ZFS') TYPE(ZFS) +
MOUNTPOINT('/u/zopen') +
MODE(RDWR) PARM('AGGRGROW') AUTOMOVE
/*

The define took less than a second – the mount took 17 seconds, because it had to do the format.

What’s the difference?

Overall the time to execute the job was the same.

I think I prefer the first way of doing it, as I have more control, and can check the format was as I expected.

If you used the second way of doing it, defined the ZFS in parmlib, I don’t know if the formatting would hold up OMVS startup.

And don’t forget

And do not forget to update your parmlib member so the ZFS is mounted automatically at IPL.

How do I securely send you a present when bad guys are intercepting our mail?

Following on from some stuff I was doing about TLS, I remembered some concept examples of security.

How do I securely send you a present when bad guys are intercepting our mail?

I want to send you a present, but I do not have a padlock from you to lock the box. If you send me a padlock – that would solve the problem – except for the bad guys intercepting the mail and replacing your padlock with theirs. I put something in the box, and lock it using the padlock I received. The bad guys open the box with their key, take out the gold bar, and put in a one pence coin – and then put your padlock on it. You open the box and are disappointed.

One way of doing it is as follows

  • I put the present in the box and put my padlock on it, and send it to you
  • You receive the box, put your padlock on it – and send the box back to me
  • I take my padlock off – and send the box to you again
  • You open the box and love the present.

The bad guys cannot get into the box (well, in real life they could).

How do we lock/unlock this gate

The traditional way is to put a chain around the gate, and put a padlock on it. You give a copy of the key to all those who need access. Every one having the same key is not a good idea. You could copy the key 100 times and give it to all your friends, and we quickly lose control of the access.

Another way is for each person to provide their own padlock. We chain the padlocks together, so we have chain, chain, my padlock, your padlock, someone else’s padlock – chain – chain.

This way we are all able to open our padlock and individually we can manage the keys (so you can make 100 copies).

How do I encrypt for multiple recipients?

If I have a 1GB record I want to send to you, I can encrypt it with your public key and send it to you. You need your private key to decrypt it. This is well known.
I want to send the 1GB record to 100 people. I could encrypt it 100 times, once per public key. This would be 100GB. The costs of this soon mounts up.

One solution is to encrypt it with a key. You then encrypt the decryption key with each person’s public key, and stick them on the front of the data. So you have 100 short blocks, followed by a 1GB encrypted block of data.

When you receive it, you iterate over the short blocks until you find one where your private key matches. You decrypt it, then used the decrypted value to decrypt the main 1GB data.

Warning brain ache ahead: Homomorpic encryption

You have been asked to create a voting system. People press one of two buttons, and your code increments the counter for each button. The requirement is that the totals for each button cannot be displayed until the voting period has finished.

Easy you think.

Store the count in an field. When you need to increment the value, decrypt it, add one, and re-encrypt it. Easy; except for the tiny weeny problem that someone with a debugger can step through the code and display the unencrypted value.

Enter Homomorphic encryption. You can do calculations on encrypted numbers.

  • You generate a special private/public key pair priv, pub = generate_keypair(128)
  • You lock the private key in a safe – with a time lock on it
  • You store the public value in your voting machine
  • You code has
    • button1 = encrypt(pub, 0)
    • button2 = encrypt(pub, 0)
    • Loop…
      • if button1 is pressed then button1 = button1 + encrypt(pub,1)
      • if button2 is pressed then button2 = button2 + encrypt(pub,1)
  • After voting has finished you do
    • print(decrypt(priv, pub, button1))
    • print(decrypt(priv, pub, button2))

Multiplication based on RSA encryption technique.

To encrypt data using RSA. You have calculate (x**public_key) Modulo N. Where N is a very large number. You can only decrypt it with the private key

  • (x **A) * (y **A) = (x*y) **A

Using RSA techniques

  • [(x **PublicKey) * (y **PublicKey)] Modulo N = [(x*y) **PublicKey ] Modulo N

To decrypt this you need the private key.

This is the “easy” case for multiplication. There are more complex schemes using Group theory and very large lattices, for addition and subtraction.

It is much more complex than I’ve explained.

How do I get my client talking to the server with a signed certificate

Signed certificates are very common, but I was asked how I connected my laptop to my server, in the scenario “one up” from a trivial example.

Basic concepts

  • A private/public key pair are generated on a machine. The private stays on the machine (securely). The public key can be sent anywhere.
  • A certificate has ( amongst other stuff)
    • Your name
    • Address
    • Public key
    • Validity dates

Getting a signed certificate

When you create a certificate: it does a checksum of the contents of the certificate, encrypts the checksum with your private key, and attaches this encrypted value to the certificate.

Conceptually, you go to your favourite Certificate Authority (UKCA) building and they Sign it

  • They check your passport and gas bill with the details of your certificate.
  • They attach the UKCA public key to your certificate.
  • They do a checksum of the combined documents.
  • They encrypt the checksum with the the UKCA private key, and stick this on the combined document.

You now have a signed certificate, which you can send it to anyone who cares.

Using it

When I receive it, and use it

  • my system compares my copy of the UKCA public certificate with the one in your certificate – it matches!
  • Using (either) UKCA public certificate – decrypt the encrypted checksum
  • Do the same checksum calculation – and the two values should match.
  • If they match I know I can trust the information in the certificate.

This means the checking of the certificate requires the CA certificate that signed it.

To use a (Linux) certificate on z/OS you either need to

  • issue the RACF GENCERT command on the Linux .csr file, export it, then download it to Linux. The certificate will contain the z/OS CA’s certificate.
  • import the Linux CA certificate into RACF (This is the easy, do once solution.)

then

  • connect the CA certificate to your keyring, and usually restart your server.

Setting up my system

If the CA certificate is not on your system, you need to import it from a dataset.

You can use FTP, or use cut and paste to the dataset.

Once you have the CA certificate in your RACF database you can connect it to your keyring.

Create my Linux CA and copy it to z/OS

CA="docca256"
casubj=" -subj /C=GB/O=DOC/OU=CA/CN=LINUXDOCCA2564"
days="-days 1095"
rm $CA.pem $CA.key.pem

openssl ecparam -name prime256v1 -genkey -noout -out $CA.key.pem

openssl req -x509 -sha384 -config caca.config -key $CA.key.pem -keyform pem -nodes $casubj -out $CA.pem -outform PEM $days

openssl x509 -in $CA.pem -text -noout|less

Where my caca.config has

####################################################################
[ req ]
distinguished_name = ca_distinguished_name
x509_extensions = ca_extensions
prompt = no

authorityKeyIdentifier = keyid:always,issuer:always

[ca_distinguished_name ]
[ ca_extensions ]

subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:TRUE, pathlen:0
keyUsage = keyCertSign, digitalSignature,cRLSign

Running the command gave

Certificate:
Data:
...
Issuer: C = GB, O = DOC, OU = CA, CN = LINUXDOCCA256
...
Subject: C = GB, O = DOC, OU = CA, CN = LINUXDOCCA256
...
X509v3 extensions:
...
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:0
X509v3 Key Usage:
Digital Signature, Certificate Sign, CRL Sign
...

Where it has CA:TRUE and X509v3 Key Usage:Certificate Sign

Which allows this to be used to sign certificates.

Installing the CA certificate on z/OS

You need to copy the docca256.pem file from Linux to a z/OS dataset (Fixed block, lrecl 80, blksize 80) you can use FTP or cut and paste. I used dataset COLIN.DOCCA256.PEM.

Import it into z/OS, and connect it to the START1.MYRING keyring as a CERTAUTH.

//COLRACF  JOB 1,MSGCLASS=H 
//S1 EXEC PGM=IKJEFT01,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
RACDCERT CHECKCERT('COLIN.DOCCA256.PEM')

*RACDCERT DELETE (LABEL('LINUXDOCA256')) CERTAUTH
RACDCERT ADD('COLIN.DOCCA256.PEM') -
CERTAUTH WITHLABEL('LINUXDOCA256') TRUST

RACDCERT CONNECT(CERTAUTH LABEL('LINUXDOCA256') -
RING(MYRING) USAGE(CERTAUTH)) ID(START1)

SETROPTS RACLIST(DIGTCERT,DIGTRING ) refresh
/*

Once you have connected the CA to the keyring, you need to get the server to reread the keyring, or restart the server.

Getting my Linux certificate signed by z/OS

This works, but is a bit tedious for a large number of certificates.

I created a certificate request file using

timeout="--connect-timeout 10"
enddate="-enddate 20290130164600Z"

ext="-extensions end_user"

name="docec384Pass2"
key="$name.key.pem"
cert="$name.pem"
p12="$name.p12"
subj="-subj /C=GB/O=Doc3/CN="$name
rm $name.key.pem
rm $name.csr
rm $name.pem
passin="-passin file:password.file"
passout="-passout file:password.file"
md="-md sha384"
policy="-policy signing_policy"
caconfig="-config ca2.config"
caextensions="-extensions clientServer"


openssl ecparam -name secp384r1 -genkey -noout -out $name.key.pem
openssl req -config openssl.config -new -key $key -out $name.csr -outform PEM -$subj $passin $passout

The certificate request file docec384Pass2.csr looks like

-----BEGIN CERTIFICATE REQUEST----- 
MIIBpzCCAS0CAQAwNDELMAkGA1UEBhMCR0IxDTALBgNVBAoMBERvYzMxFjAUBgNV
...
Tmmvu/nqe0wTc/jJuC4c/QJt+BQ1SYMxz9LiYjBXZuOZkpDdUieZDbbEew==
-----END CERTIFICATE REQUEST-----

With words CERTIFICATE REQUEST in the header and trailer records.

Create a dataset(COLIN.DOCLCERT.CSR) with the contents. It needs to be a sequential FB, LRECL 80 dataset.

  • Delete the old one
  • Generate the certificate using the information in the .csr. Sign it with the z/OS CA certificate
  • Export it to a dataset.
//IBMRACF2 JOB 1,MSGCLASS=H 
//S1 EXEC PGM=IKJEFT01,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *

RACDCERT ID(COLIN ) DELETE(LABEL('LINUXCERT'))

RACDCERT ID(COLIN) GENCERT('COLIN.DOCLCERT.CSR') -
SIGNWITH (CERTAUTH LABEL('DOCZOSCA')) -
WITHLABEL('LINUXCERT')

RACDCERT ID(COLIN) LIST(label('LINUXCERT'))
RACDCERT EXPORT(label('LINUXCERT')) -
id(COLIN) -
format(CERTB64 ) -
password('password') -
DSN('COLIN.DOCLCERT.PEM' )

SETROPTS RACLIST(DIGTCERT,DIGTRING ) refresh

Then you can download COLIN.DOCLCERT.PEM to a file on Linux and use it. I used cut and paste to create a file docec384Pass2.zpem

I used it like

set -x 
name="colinpaice"
name="colinpaice"
name="docec384Pass2"
insecure=" "
insecure="--insecure"
timeout="--connect-timeout 100"
url="https://10.1.1.2:10443"
trace="--trace curl.trace.txt"

cert="--cert ./$name.zpem:password"
key="--key $name.key.pem"

curl -v $cert $key $url --verbose $timeout $insecure --tlsv1.2 $trace

Using wireshark I can see CA certificates being send from z/OS, and the docec384Pass2.lpem used; signed by a z/OS CA certificate.

Using the certificate in the Chrome browser.

  • In Chrome settings, search for cert.
  • Click security
  • Scroll down to Manage certificates, and select it
  • Select customised
  • Select import, and then select the file.
    • When I generated the file with the Linux CA it had a file type of .pem
    • When I signed it on z/OS, then downloaded it with a type of.zpem, I had to select all files (because the defaults are *.pem,*.csr,*.der..)