MFA installation and configuration

Other posts on MFA:

The documentation for the configuration is pretty good, I’ll document additional things that I discovered. I ran MFA on z/OS system provided as part of ADCD and zPDT, so your configuration may be different.

If you add and configure a new factor, such as adding Timed One Time Password AZFTOTP1 you need to restart the AZF#IN00 started task. It takes a couple of seconds to restart the MFA started tasks.

You define a policy for Out-of-band authentication which has a list of factors to be used to authenticate. The factors must have been configured before you can specify them in a policy.

You can specify a default policy in the web services started task configuration for the out of bound authentication.

Create a mount point for the ZFS, if needed

In OMVS issue the command

ls /usr/lpp/IBM/azfv2r2

If this is not defined then use the mkdir /usr/lpp/IBM/azfv2r2 command.

If this is defined, and has content, then the ZFS has been mounted.

If this is defined and has no content, the you need to mount the ZFS, see Create USER.Z25D.PARMLIB(BPXPRMMF) below. You can use the TSO mount command to mount if for the duration of the IPL.

Update parmlib

Once you have created the parmlib members below, I found it easiest, on my one person z/OS system, to re-ipl. You can activate these members without an IPL, by using the operator command SET OMVS=CP, SET PROG=CP, and SET SCH=(00,ZW,MF)

It takes several steps to update LNKLST. See here. The operator commands are

SETPROG LNKLST,DEFINE,NAME=MYLNKLST,COPYFROM=CURRENT
SETPROG LNKLST,ADD,NAME=MYLNKLST,DSN=AZF220.SAZFLOAD,VOLUME=D5PRD4
SETPROG LNKLST,ACTIVATE,NAME=MYLNKLST

Create USER.Z25D.PARMLIB(BPXPRMMF)

MOUNT FILESYSTEM('AZF220.ZFS') TYPE(ZFS) 
MOUNTPOINT('/u/mfa') NOAUTOMOVE
MODE(RDWR) PARM('AGGRGROW')

Create USER.Z25D.PARMLIB(SCHEDMF)

PPT PGMNAME(AZFSTCMN) /* MULTI-FACTOR AUTH */ 
KEY(2) /* PROTECTION KEY */
NOSWAP /* NON-SWAPABLE */
CANCEL /* CANCELABLE */

USER.Z25D.PARMLIB(PROGMF)

Update (or create) your PROGxx member

LNKLST ADD NAME(LNKLST00) DSN(AZF220.SAZFLOAD)           VOLUME(D5PRD4) 
APF ADD DSNAME(AZF220.SAZFLOAD) VOLUME(D5PRD4)

Update USER.Z25D.PARMLIB(IEASYSCP)

You need to update your IEAYSxx member. If you do not have one in USER.Z25D.PARMLIB, copy IEASYS00 from ADCD.Z25D.PARMLIB and update that.

PROG=(AB,AM,A0,A2,ZW,MF,     DYNAMIC APF LIST 
SY,LB,LM,L0,L2,CP,LL), LINKLIST LL SUFFIX SHOULD BE LAST
SCH=(00,ZW,MF), SELECT SCHED00

Creating a configuration

Having been through the process a couple of times I have some JCL which makes it easier to do the RACF configuration. It uses JCL substitution so you only need to define the values once in the JCL.

Member PARMS

This defines common values, used in more than one place

//  SET OWNER='IBMUSER' to own the resource 
// SET ADMIN='IBMUSER' uses AZFEXEC can be group or userid
// SET TOKEN='MFATOKEN'
// SET STC='AZFWEB' Started Task userid
// SET READER='COLIN' This id/group can browse AZFEXEC
// SET KEYRING='START1.TN3270' used in AT-TLS. STC needs access

Every job needs

//IBMUSERT JOB 1,MSGCLASS=H,RESTART=S1 
// JCLLIB ORDER=COLIN.MFA.JCL4
// EXPORT SYMLIST=(*)
// INCLUDE MEMBER=PARMS

to make the variables available to the configuration.

Configure once-off member SYSJ JCL

//IBMUSERT JOB 1,MSGCLASS=H,RESTART=S1 
//* Job to do initial, one time set up
// JCLLIB ORDER=COLIN.MFA.JCL4
// EXPORT SYMLIST=(*)
// INCLUDE MEMBER=PARMS
//*
//* DEFINE THE STUFF NEEDED FOR STC
//S1 EXEC PGM=IKJEFT01,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *,SYMBOLS=(JCLONLY)
// INCLUDE MEMBER=SYSAZF

Where SYSAZF has

//         DD   *,SYMBOLS=JCLONLY 
*DEL FACILITY IRR.RFACTOR.USER
RDEF FACILITY IRR.RFACTOR.USER OWNER(&OWNER)
PERMIT IRR.RFACTOR.USER ACCESS(UPDATE) CLASS(FACILITY) ID(&STC)

*DEL FACILITY IRR.RFACTOR.USER
RDEF FACILITY IRR.RFACTOR.USER UACC(NONE) OWNER(&OWNER)

*DEL MFADEF FACTOR.AZFSTC
RDEF MFADEF FACTOR.AZFSTC OWNER(&OWNER)

Define started task userid member UAZFWEBJ JCL

  • If you want to use an existing userid, delete the DELUSER and the ADDUSER statements
  • NOPASSWORD is specified so it cannot be logged on to.
  • It defines the started class. The STDATA has the userid to use.
  • The userid needs to be able to list keyrings.
  • It defines an RDATALIB profile, and gives the userid update access to the ring, so that the started task userid can access any private certificate in the keyring.

This uses member UAZFWEB

//         DD   *,SYMBOLS=JCLONLY 
*ELUSER &STC
ADDUSER &STC DFLTGRP(TEST) OWNER(&OWNER) -
NAME('MFA STC') NOPASSWORD

RDELETE STARTED AZF#IN*.**
RDEFINE STARTED AZF#IN*.** STDATA(USER(&STC ))
RLIST STARTED AZF#IN*.**
SETR RACLIST(STARTED) REFRESH

PERMIT IRR.DIGTCERT.LISTRING CL(FACILITY) ID(&STC) ACCESS(UPDATE)
SETR RACLIST(FACILITY) REFRESH

and member UAZFRING (define access to the ring)

//         DD   *,SYMBOLS=JCLONLY 
*
* define the profile for RDATALIB
* This may already exist in your organisation

RDEFINE RDATALIB &KEYRING..LST UACC(NONE)

* Userid needs update access to be able to see other's private certs
PERMIT &KEYRING..LST CLASS(RDATALIB) -
ID(&STC) ACCESS(UPDATE)

SETROPTS RACLIST(RDATALIB) REFRESH

Define the CSF* profiles and give permissions job CSFPROFJ

This contains the statements to define the CSF permissions. You should review the member and edit it, as some of the profiles may exist already. Do not create the more generic CSF* if there are more specific profiles. I found it useful to have the WARNING option, as this give you permission to the resource, and gives you a message. Once you have configured MFA you can reset these to NOWARNING.

//         DD   *,SYMBOLS=JCLONLY 
RLIST CSFSERV CSF1TRC
RLIST CSFSERV CSFOWH
RLIST CSFSERV CSF1TRL
RLIST CSFSERV CSF*
RLIST CSFKEYS *

RDEFINE CSFSERV CSFOWH UACC(NONE) OWNER(&OWNER)
RDEFINE CSFSERV CSF1TRC UACC(NONE) OWNER(&OWNER)
RDEFINE CSFSERV CSF1TRL UACC(NONE) OWNER(&OWNER)
RDEFINE CSFSERV CSF* UACC(NONE) OWNER(&OWNER)
RDEFINE CSFKEYS * UACC(NONE) OWNER(&OWNER)
PERMIT CSF1TRC CLASS(CSFSERV) ID(&ADMIN ) ACC(CONTROL)
PERMIT CSF1TRC CLASS(CSFSERV) ID(&STC ) ACC(READ )
PERMIT CSF1TRL CLASS(CSFSERV) ID(&ADMIN ) ACC(CONTROL)
PERMIT CSF1TRL CLASS(CSFSERV) ID(&STC ) ACC(READ )
PERMIT CSF* CLASS(CSFSERV) ID(&STC ) ACC(READ )

PERMIT CSFOWH CLASS(CSFSERV) ID(&STC ) ACC(READ )

RALTER CSFKEYS * WARNING
RALTER CSFSERV CSF* WARNING
RALTER CSFSERV CSF1TRC WARNING

SETROPTS RACLIST(CSFSERV,CSFKEYS) REFRESH

RLIST CSFKEYS * ALL
RLIST CSFSERV * ALL

Member CSFPROT defines the profiles to use the ICSF token, and adds the token.

//         DD   *,SYMBOLS=JCLONLY  OWNER(&OWNER) 
RLIST CRYPTOZ SO.&TOKEN
RLIST CRYPTOZ USER.&TOKEN
RLIST CRYPTOZ CLEARKEY.&TOKEN
RDEFINE CRYPTOZ SO.&TOKEN UACC(NONE) OWNER(&OWNER)
RDEFINE CRYPTOZ USER.&TOKEN UACC(NONE) OWNER(&OWNER)
RDEFINE CRYPTOZ CLEARKEY.&TOKEN UACC(NONE)

PERMIT SO.&TOKEN CLASS(CRYPTOZ) ID(&ADMIN) ACC(CONTROL)
PERMIT SO.&TOKEN CLASS(CRYPTOZ) ID(&STC ) ACC(CONTROL)

PERMIT USER.&TOKEN CLASS(CRYPTOZ) ID(&ADMIN) ACC(UPDATE)
PERMIT USER.&TOKEN CLASS(CRYPTOZ) ID(&STC) ACC(UPDATE)

PERMIT CLEARKEY.&TOKEN CLASS(CRYPTOZ) ID(&ADMIN) ACC(READ)
PERMIT CLEARKEY.&TOKEN CLASS(CRYPTOZ) ID(&STC ) ACC(READ)

RACDCERT LISTTOKEN(&TOKEN)
RACDCERT ADDTOKEN(&TOKEN)

Define factor(s) using JCL FACTORJ

The JCL below defines 4 factors, you can easily change or extend this list.

//IBMUSERF JOB 1,MSGCLASS=H,RESTART=S1 
// JCLLIB ORDER=COLIN.MFA.JCL4
// EXPORT SYMLIST=(*)
// INCLUDE MEMBER=PARMS
//* Job to define some factors and groups
//*
//FACT PROC FACTOR=XXXXXX,GROUP=XXXXXXXX
// SET FACTOR=&POLICY
// SET GROUP=&GROUP
//S1 EXEC PGM=IKJEFT01,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *,SYMBOLS=(JCLONLY)
//* INCLUDE MEMBER=FACTORD
// INCLUDE MEMBER=FACTORA
// PEND
//*
// EXEC PROC=FACT,FACTOR=AZFUSER1,GROUP=AZFGUSER
// EXEC PROC=FACT,FACTOR=AZFCERT1,GROUP=AZFGCERT
// EXEC PROC=FACT,FACTOR=AZFTOTP1,GROUP=AZFGTOTP
// EXEC PROC=FACT,FACTOR=AZFYUBI1,GROUP=AZFGYUBI

Member FACTORA contains

//         DD   *,SYMBOLS=JCLONLY 
LG &GROUP
* If group exists, addgroup will fail with IKJ56702I INVALID GROUP
ADDGROUP (&GROUP) OWNER(&OWNER)

RDEF MFADEF FACTOR.&FACTOR OWNER(&OWNER)
RDEF FACILITY IRR.RFACTOR.MFADEF.&FACTOR OWNER(&OWNER)
PERMIT IRR.RFACTOR.MFADEF.&FACTOR ACCESS(ALTER) CLASS(FACILITY) -
ID(&ADMIN)
PERMIT IRR.RFACTOR.MFADEF.&FACTOR ACCESS(READ) CLASS(FACILITY) -
ID(&STC)
PERMIT IRR.RFACTOR.MFADEF.&FACTOR ACCESS(READ) CLASS(FACILITY) -
ID(&GROUP)

SETROPTS RACLIST(MFADEF) REFRESH
SETROPTS RACLIST(FACILITY) REFRESH

It defines a group for users, creates the resource, and gives the administrator,started task, and members of the group access to the resource.

Member FACTORD deletes the resources

//         DD   *,SYMBOLS=JCLONLY 
* DELGROUP &GROUP
RDEL MFADEF FACTOR.&FACTOR
RDEL FACILITY IRR.RFACTOR.MFADEF.&FACTOR

Once you have configured the factors you need to use AZFEXEC to configure the STC, and the factors you have defined.

Once you have created the factors, you can start AZF#IN00.

Create policies using member POLICYJ JCL

You use a policy for out of band authentication.

This job has

//IBMUSERT JOB 1,MSGCLASS=H,RESTART=S1 
// JCLLIB ORDER=COLIN.MFA.JCL4
// EXPORT SYMLIST=(*)
// INCLUDE MEMBER=PARMS
//* Job to define some policies
//*
//POL PROC POLICY=XXXXXX,F1=' ',F2=' ',F3=' ',F4=' '
// SET POLICY=&POLICY
// SET F1=&F1
// SET F2=&F2
// SET F3=&F3
// SET F4=&F4
//S1 EXEC PGM=IKJEFT01,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *,SYMBOLS=(JCLONLY)
// INCLUDE MEMBER=POLICYD
// INCLUDE MEMBER=POLICYA
// PEND
//*
// EXEC PROC=POL,POLICY=OOBTOUS,F1=AZFTOTP1,F2=AZFUSER1
// EXEC PROC=POL,POLICY=OOBTOTP,F1=AZFTOTP1

Where F1,F2,F3 and F4 are the factors. Leave them blank if you do not want so many factors.

This creates two policies. OOBTOUS uses factor AZFTOTP1 and AZFUSER1

Member POLICYD deletes a policy

//         DD   *,SYMBOLS=JCLONLY 
RDEL MFADEF POLICY.&POLICY
RDEL FACILITY IRR.RFACTOR.POLICY.&POLICY
SETROPTS RACLIST(MFADEF) REFRESH
SETROPTS RACLIST(FACILITY) REFRESH

Member POLICYA adds a policy. If you use POLICYD before, it will delete it first

//         DD   *,SYMBOLS=JCLONLY 
RDEF MFADEF POLICY.&POLICY OWNER(&OWNER) -
MFPOLICY(FACTOR(&F1 &F2 &F3 &F4) TOKENTIMEOUT(60) REUSE(N))

PERMIT POLICY.&POLICY CLASS(MFADEF) ACCESS(READ) -
ID(&STC)

RDEF FACILITY IRR.RFACTOR.POLICY.&POLICY UACC(NONE) -
OWNER(&OWNER)

PERMIT IRR.RFACTOR.POLICY.&POLICY ACCESS(READ) CLASS(FACILITY) -
ID(&STC)

SETROPTS RACLIST(FACILITY) REFRESH
SETROPTS RACLIST(MFADEF) REFRESH

Create a userid to test it JCL member USERJ

You need to have defined the factors you want to use, configured the factors, and start the AZF#IN00 started task before you can configure userids.

//IBMUSERT JOB 1,MSGCLASS=H,RESTART=S1 
//* Job to do set up userid coonfigure all attr and give policy
// JCLLIB ORDER=COLIN.MFA.JCL4
// EXPORT SYMLIST=(*)
// INCLUDE MEMBER=PARMS
// SET USERID=AZFUSER
// SET POLICY1=OOBTOTP
// SET POLICY2=OOBTOUS

// SET POLICY3=OOBTOUS
// SET POLICY4=OOBTOUS
// SET RSAUSER='??????'
// SET RADUSER='??????'
// SET ALTUSER='??????'
// SET WINDOW='??????'
//*
//* DEFINE THE STUFF NEEDED FOR STC
//S1 EXEC PGM=IKJEFT01,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *,SYMBOLS=(JCLONLY)
// INCLUDE MEMBER=USERTSO
// INCLUDE MEMBER=USERAZF
// INCLUDE MEMBER=USERPOL

Member USERTSO adds a userid, adds a TSO segment (so you can easily test it).

Member USERAZF has all of the ALU profiles needed to define an MFA segment – though some are commented out. I find it easier to delete lines you do not need, than type the information in.

For example

//         DD   *,SYMBOLS=JCLONLY 
ALU &USERID MFA(FACTOR(AZFYUBI1) NOACTIVE NOPWFALLBACK NOTAGS)
ALU &USERID MFA(FACTOR(AZFTOTP1) TAGS(REGSTATE:OPEN))
ALU &USERID MFA(FACTOR(AZFYUBI1) ACTIVE )

ALU &USERID MFA(FACTOR(AZFPASS1) ACTIVE )

ALU &USERID MFA(FACTOR(AZFTOTP1) NOACTIVE NOTAGS))
ALU &USERID MFA(FACTOR(AZFTOTP1) TAGS(REGSTATE:OPEN))
ALU &USERID MFA(FACTOR(AZFTOTP1) TAGS(ALG:SHA1))
...
*ALU &USERID MFA(FACTOR(AZFPTKT1) ACTIVE )

Delete AZF profiles. JCL member CLEANJ

This job uses member CLEAN which deletes the profiles.

//         DD   *,SYMBOLS=JCLONLY 
DELGROUP (AZFGUSER)
...

RDEL MFADEF FACTOR.AZFCERT1
...
RDEL FACILITY IRR.RFACTOR.FACILITY.AZFCERT1

The configuration process

Chapter 4. System programming steps

  • Copy SAZFEXEC(AZFEXEC)
  • Customize AZFEXEC
  • Copy SAZFSAMP(AZF#IN00) and SAZFSAMP(AZF#IN01). You may wish to rename these procs. If you do, you’ll need to change the RACF STARTED definitions
  • Authorize the Load Library
  • Add SAZFLOAD to the link list. I found it easier to update the parmlib member and re-IPL.
  • Update SCHEDxx PARMLIB program properties
  • Set the WLM service class

What next?

You need to define at least authentication factor configured before MFA will successfully start. One Time Passcode is an easy one to define.

My approach to getting MFA configured for user is:

  • Get one function working such as Timed One Time Password (TOTP) so you can logon with just TOTP (and no password)
  • Change the configuration to use Compound In-band Authentication, where you need TOTP and a password, so you enter 123456:passw0rd. You need to decide if you want password:code or code:password, and what the separator character is.
  • Develop a policy for out-of-band authentication for applications which do not accept a long or compound password.
  • Deploy it to users.

One minute MVS: What is IBM Multi Factor Authentication on z/OS?

Most people are familiar with Multi Factor Authentication (MFA). For example when accessing a banking site through the internet, you have a digital code sent to your phone which you enter in the web page.

There is a phrase associated with MFA. Something you know, something you have. When using internet banking, you use a userid and password(something you know) and the 6 digit code sent to your phone (something you have). At airports, the staff use a badge to get access to secure areas. They swipe the badge (something you have) and have to enter a 4 digit code (something you know).

In-band and out-of-band

With some applications you enter two factors to logon to the application. For example, I can logon to TSO with a “password” 983211:passw0rd - where 983211 is a one time code (which changes every minute) and passw0rd is my password. This is in-band (you enter the combined password >IN<to the application).

I can use a certificate to logon to a web page, get a one time password and enter that into the TSO logon screen. This is indirect, or out-of-band authentication, you set up the password >OUT<side of the application.

What is available on z/OS?

You can set up one-time-codes, or password (or pass phrase), or one-time-code and password (or pass phrase). A password can be up to 8 characters. A pass phrase must be between 14 to 100 characters in length (inclusive).

You can get a one-time-code from several sources:

  • A small hardware device, which you can hang on your keyring
  • Generated from software. I use the IBM Security Verify application on my Android phone. There are other applications, such as Google Authenticator and Duo Mobile, but the code generated by these was not accepted by z/OS. See below.

To set up the mobile phone application you logon on the IBM MFA web browser on your z/OS and get a QR code displayed. This code contains a secret, and other information such as algorithm=SHA256, period=60 seconds, digits=6. The app on your phone reads this and stores the information. When ever you use the app, it displays a code which you enter on z/OS. The value is time limited and expires after a short interval, typically 30 or 60 seconds.

I configured MFA to use just the TOTP (One Time Password). When I logged on to TSO with userid TOTP and the code I got

ICH70008I IBM MFA Message:
AZF1105I TOTP PASSCODE ACCEPTED
ICH70001I TOTP LAST ACCESS AT 07:37:37 ON SATURDAY, JANUARY 6, 2024

When I configured MFA to require TOTP and userid, I had to enter a password like 345112:PASSW0RD, where 345112 was the one time code from the application.

You configure the MFA on a per userid basis. I set up MFA for a new userid called TOTP, and this has to logon with two factors. Another userid only has to logon with the password.

The IBM Security verify application worked out of the box.

With applications Duo Mobile, and Google Authenticator I got message

AZF5042E Preflight saw invalid account metadata

because they provided an invalid code. The applications only worked with the following system wide options

  • Digest Algorithm . . . . . 1 (SHA-1)
  • Token Code Length . .. . 1 (6-digit)
  • Token Period. . . . . . . . . 2 (30 seconds)

See this page for more information.

Yubikey

A Yubikey is a small USB device from which you can get a one time password. I found the site and what you need to order confusing, and purchased the wrong device. On one of the Yubikey pages it compares the different devices. I needed a Yubikey 5 series; I had (wrongly) purchased a Yubikey Security key Series. When the new key arrives I’ll write up how to use it.

Using a certificate

To make your certificate known to the MFA instance, you logon to a web page using TLS. The web browser port has been secured using AT-TLS. When you logon to the web page, the web browser displays the list of valid certificates for you to choose. After you have selected one, the application running in the web server can extract information from the certificate, and update the userid information in the MFA profile for the userid.

You set up an “out-of-bounds policy” saying use which authentication method (use certificates) and how long the password is valid for (60 seconds).

You configure the userid to be able to use the policy.

To be able to logon, the userid logs on to a different web page ../mfa/mypolicy using the same certificate and enters the userid. A TLS handshake is done to the server (validating the certificate), and a password is returned. You enter the password in your application.