Other posts on MFA:
- Multi Factor Authentication(MFA): Planning.
- MFA: installation and configuration
- MFA: Configuring a userid
- MFA: displaying information
- MFA: configuring Timed One Time Password (TOTP)
- MFA: Using a password
- MFA configuring a policy for out of band authentication
- MFA: configuring Yubikey
- MFA: setting up Linux as an authenticator to generate a TOTP password
- MFA: messages
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.
10 thoughts on “MFA installation and configuration”