Beginning with version 5.1.21 a lot of additional checks against a System authorization facility (SAF) can be activated. These SAF support gives for example an RACF administrator on z/OS the possibility to activate different security policies and control a lot of resources used by FLAM.
The complete SAF setup is documented in the corresponding install.txt and can be simply extended on user request. For z/OS with RACF we recommend at minimum the definitions below (be careful with this example, we are no RACF specialists):
//S01 EXEC PGM=IKJEFT01,DYNAMNBR=75,TIME=100,REGION=6M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTERM DD DUMMY
//SYSUADS DD DSN=SYS1.UADS,DISP=SHR
//SYSLBC DD DSN=SYS1.BRODCAST,DISP=SHR
//SYSTSIN DD *
RDEFINE CDT +
$FLAM +
CDTINFO(CASE(ASIS) /* mixed case profile names */ +
DEFAULTRC(4) /* default */ +
DEFAULTUACC(NONE) /* default */ +
FIRST(ALPHA) /* profile name first char */ +
GENERIC(ALLOWED) /* default */ +
KEYQUALIFIERS(0) /* default */ +
MAXLENGTH(246) /* maximum prof name length */ +
OPERATIONS(NO) /* default */ +
OTHER(ALPHA, /* prof name allows A-Z,a-z */ +
NUMERIC, /* 0-9 */ +
NATIONAL, /* #,@,$ */ +
SPECIAL) /* all except blank ( ) , ; */ +
POSIT(202) /* in user range 128-527 */ +
RACLIST(ALLOWED) /* setr raclist allowed */ +
SECLABELSREQUIRED(NO) /* default */ +
SIGNAL(NO) /* default (no ENF signals) */ +
)
SETROPTS CLASSACT(CDT)
SETROPTS RACLIST(CDT)
SETROPTS RACLIST(CDT) REFRESH
//S02 EXEC PGM=IKJEFT01,DYNAMNBR=75,TIME=100,REGION=6M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTERM DD DUMMY
//SYSUADS DD DSN=SYS1.UADS,DISP=SHR
//SYSLBC DD DSN=SYS1.BRODCAST,DISP=SHR
//SYSTSIN DD *
PERMIT $FLAM CLASS(CDT) RESET /* clear permit list */
RALTER CDT $FLAM OWNER(SYS1) /* change ownership */
SETROPTS RACLIST(CDT) REFRESH
//S03 EXEC PGM=IKJEFT01,DYNAMNBR=75,TIME=100,REGION=6M
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTERM DD DUMMY
//SYSUADS DD DSN=SYS1.UADS,DISP=SHR
//SYSLBC DD DSN=SYS1.BRODCAST,DISP=SHR
//SYSTSIN DD *
SETROPTS GENERIC($FLAM)
RDEFINE $FLAM +
** /* cover all entities */ +
OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.MUSTDEF OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.POLICIES OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.SMF.LOGGING OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.CLP.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.CMD.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.AVE.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.CPE.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.KME.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.SGU.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.OBU.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.EDC.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.FIO.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.FL4.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.ARC.CONTROL OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.SSH.CONNECT OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.RST.CONNECT OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.PGP.KEYMNGM OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.PGP.USERID OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM GLOBAL.PGP.KEYID OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.LOW.ENTROPY OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.LOG.INPUT OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.SSH.AUTH.*.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.SSH.PCAP.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.SSH.WEAK.HOSTKEY.CHECK.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.SSH.CUSTOM.HOSTKEY.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.SSH.CUSTOM.KNOWN.HOST.FILE.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.SSH.CUSTOM.CONFIG.FILE.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.KEY.GENERATE.PGP.EXPIRD.REQIRED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.KEY.GENERATE.PGP.ALGO.RSA.KEYLEN OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.KEY.GENERATE.PGP.ALGO.DSA.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.KEY.IMPORT.PGP.KIDVER OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.KEY.IMPORT.PGP.DOUBLE.KIDS OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.PGP.PWD OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.PGP.PWD.LEN.ENC OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.PGP.PWD.LEN.DEC OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.PGP.ENFORCE.ENCRYPTION OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.PGP.ENFORCE.INTEGRITY OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.PGP.ENFORCE.SIGNING OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.EDC.PARM.FILE.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.EDC.KEY.PRINT.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.EDC.KEY.INVERSE.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.EDC.NO.HEADER.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.EDC.NO.SALT.ALLOWED OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.SYM.KEY.LEN.ENC OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.SYM.KEY.LEN.DEC OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.SYM.PWD.LEN.ENC OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.SYM.PWD.LEN.DEC OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.ARC.ENCRYPT OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.ARC.PROTECT OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.ARC.PGP OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.ARC.PWD OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.ARC.DEF.PWD OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.ARC.PWD.LEN.ENC OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM POLICY.ARC.PWD.LEN.DEC OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM FAVELIB.libfave OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM FAVEFUC.CLAMAVC OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM FAVEPAR.clamd.limes.de:3310 OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM FAVEIPH.clamd.limes.de OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM FAVEIPS.3310 OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM FSEGLIB.LIBFSEG OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM FSEGFUC.FSEGUSR OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM FOBJLIB.LIBFOBJ OWNER(SYS1) UACC(NONE)
RDEFINE $FLAM FOBJFUC.FOBJUSR OWNER(SYS1) UACC(NONE)
PERMIT ** CLASS($FLAM) RESET /* clear permit list (optional)*/
PERMIT GLOBAL.MUSTDEF CLASS($FLAM) ID(*) ACC(CONTROL)
/* PERMIT GLOBAL.POLICIES CLASS($FLAM) ID(*) ACC(NONE) ACTIVE */
PERMIT GLOBAL.SMF.LOGGING CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
PERMIT GLOBAL.CLP.CONTROL CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
PERMIT GLOBAL.CMD.CONTROL CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
/* PERMIT GLOBAL.AVE.CONTROL CLASS($FLAM) ID(*) ACC(NONE) ACTIVE */
PERMIT GLOBAL.CPE.CONTROL CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
PERMIT GLOBAL.KME.CONTROL CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
PERMIT GLOBAL.SGU.CONTROL CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
PERMIT GLOBAL.OBU.CONTROL CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
PERMIT GLOBAL.EDC.CONTROL CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
PERMIT GLOBAL.FIO.CONTROL CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
/* PERMIT GLOBAL.FL4.CONTROL CLASS($FLAM) ID(*) ACC(ALTER) ACTIVE */
/* PERMIT GLOBAL.ARC.CONTROL CLASS($FLAM) ID(*) ACC(ALTER) ACTIVE */
PERMIT GLOBAL.SSH.CONNECT CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
PERMIT GLOBAL.RST.CONNECT CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
PERMIT GLOBAL.PGP.KEYMNGM CLASS($FLAM) ID(*) ACC(ALTER)/*INACTIV*/
PERMIT GLOBAL.PGP.USERID CLASS($FLAM) ID(*) ACC(ALTER) /*INACTIV*/
PERMIT GLOBAL.PGP.KEYID CLASS($FLAM) ID(*) ACC(ALTER) /*INACTIV*/
PERMIT POLICY.SSH.AUTH.PUBLICKEY CLASS($FLAM) ID(*) ACC(READ)
PERMIT POLICY.SSH.AUTH.PASSWORD CLASS($FLAM) ID(*) ACC(UPDATE)
/* PERMIT POLICY.SSH.PCAP.ALLOWED CLASS($FLAM) ID(*) ACC(NONE)*/
PERMIT POLICY.SSH.WEAK.HOSTKEY.CHECK.ALLOWED CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT POLICY.SSH.CUSTOM.HOSTKEY.ALLOWED CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT POLICY.SSH.CUSTOM.KNOWN.HOST.FILE.ALLOWED CLASS($FLAM) ID(*) ACC(CONTRL)
PERMIT POLICY.SSH.CUSTOM.CONFIG.FILE.ALLOWED CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT POLICY.KEY.GENERATE.PGP.EXPIRD.REQIRED CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT POLICY.KEY.GENERATE.PGP.ALGO.RSA.KEYLEN CLASS($FLAM) ID(*) ACC(UPDATE)
/* PERMIT POLICY.KEY.GENERATE.PGP.ALGO.DSA.ALLOWED CLASS($FLAM) ID(*) ACC(NONE)*/
PERMIT POLICY.KEY.IMPORT.PGP.KIDVER CLASS($FLAM) ID(*) ACC(UPDATE)
/* PERMIT POLICY.KEY.IMPORT.PGP.DOUBLE.KIDS CLASS($FLAM) ID(*) ACC(READ)*/
PERMIT POLICY.PGP.PWD CLASS($FLAM) ID(*) ACC(ALTER)
PERMIT POLICY.PGP.PWD.LEN.ENC CLASS($FLAM) ID(*) ACC(CONTRL)
PERMIT POLICY.PGP.PWD.LEN.DEC CLASS($FLAM) ID(*) ACC(ALTER)
/* PERMIT POLICY.PGP.ENFORCE.ENCRYPTION CLASS($FLAM) ID(*) ACC(NONE)*/
/* PERMIT POLICY.PGP.ENFORCE.INTEGRITY CLASS($FLAM) ID(*) ACC(NONE)*/
PERMIT POLICY.PGP.ENFORCE.SIGNING CLASS($FLAM) ID(*) ACC(ALTER)
PERMIT POLICY.EDC.PARM.FILE.ALLOWED CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT POLICY.EDC.KEY.PRINT.ALLOWED CLASS($FLAM) ID(*) ACC(UPDATE)
/* PERMIT POLICY.EDC.KEY.INVERSE.ALLOWED CLASS($FLAM) ID(*) ACC(NONE)*/
PERMIT POLICY.EDC.NO.HEADER.ALLOWED CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT POLICY.EDC.NO.SALT.ALLOWED CLASS($FLAM) ID(*) ACC(READ)
PERMIT POLICY.EDC.STATIC.KEY.ALLOWED CLASS($FLAM) ID(*) ACC(ALTER)
PERMIT POLICY.EDC.STATIC.IV.ALLOWED CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT POLICY.SYM.KEY.LEN.ENC CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT POLICY.SYM.KEY.LEN.DEC CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT POLICY.SYM.PWD.LEN.ENC CLASS($FLAM) ID(*) ACC(CONTRL)
PERMIT POLICY.SYM.PWD.LEN.DEC CLASS($FLAM) ID(*) ACC(ALTER)
PERMIT POLICY.ARC.ENCRYPT CLASS($FLAM) ID(*) ACC(CONTROL)
PERMIT POLICY.ARC.PROTECT CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT POLICY.ARC.PGP CLASS($FLAM) ID(*) ACC(ALTER)
PERMIT POLICY.ARC.PWD CLASS($FLAM) ID(*) ACC(ALTER)
PERMIT POLICY.ARC.DEF.PWD CLASS($FLAM) ID(*) ACC(NONE)
PERMIT POLICY.ARC.PWD.LEN.ENC CLASS($FLAM) ID(*) ACC(CONTRL)
PERMIT POLICY.ARC.PWD.LEN.DEC CLASS($FLAM) ID(*) ACC(ALTER)
PERMIT FAVELIB.libfave CLASS($FLAM) ID(*) ACC(READ)
PERMIT FAVEFUC.CLAMAVC CLASS($FLAM) ID(*) ACC(READ)
PERMIT FAVEPAR.clamd.limes.de:3310 CLASS($FLAM) ID(*) ACC(READ)
PERMIT FAVEIPH.clamd.limes.de CLASS($FLAM) ID(*) ACC(READ)
PERMIT FAVEIPS.3310 CLASS($FLAM) ID(*) ACC(READ)
PERMIT FSEGLIB.LIBFSEG CLASS($FLAM) ID(*) ACC(READ)
PERMIT FSEGFUC.FSEGUSR CLASS($FLAM) ID(*) ACC(READ)
PERMIT FOBJLIB.LIBFOBJ CLASS($FLAM) ID(*) ACC(READ)
PERMIT FOBJFUC.FOBJUSR CLASS($FLAM) ID(*) ACC(READ)
/* REST/HTTP resource access control (active when GLOBAL.RST.CONNECT */
/* has no ALTER permit -- see above). Resource entity is the URL. */
/* Use specific URL profiles (e.g. RST.GET.https://s3.example.com/**) */
/* instead of ** wildcards for production environments. */
PERMIT RST.CONNECT.** CLASS($FLAM) ID(*) ACC(CONTROL)
/* The same resource gates a Cloud Data Access provider, because the */
/* question is the same one -- may this caller reach that place. CDA does */
/* not run through the REST layer, so the entity is not a URL: */
/* RST.CONNECT.ibmcda://<provider> */
/* The ** profile above already covers it. To decide per provider instead: */
/* PERMIT RST.CONNECT.ibmcda://** CLASS($FLAM) ID(*) ACC(NONE) */
/* PERMIT RST.CONNECT.ibmcda://COSPROD CLASS($FLAM) ID(archiver) */
/* ACC(CONTROL) */
/* The bucket is not part of the entity: what a user id may do inside a */
/* provider follows from the credential in its CDA key file, which RACF */
/* governs through CSFKEYS. */
/* PERMIT RST.NOVERIFY.** CLASS($FLAM) ID(*) ACC(NONE) (forbid TLS bypass) */
/* The TLS floor. A LADDER, inverted on purpose: the weakest level costs */
/* the highest access, and RACF access is hierarchical, so the permitted */
/* level IS the floor. UPDATE is the recommended setting -- TLS 1.2 and */
/* above. Grant ALTER only on the one endpoint that needs TLS 1.0: */
/* PERMIT RST.TLSVSN.https://legacy.example.com CLASS($FLAM) */
/* ID(flam_users) ACC(ALTER) */
/* With no permit at any level no TLS version is offered and the */
/* connection fails. There is no environment variable for this: the */
/* permit is the configuration. */
PERMIT RST.TLSVSN.** CLASS($FLAM) ID(*) ACC(UPDATE)
/* The cipher list, checked only when FL_HWTHTTP_CIPHERS names one. The */
/* level asked for is what the WEAKEST suite in the list demands, so a */
/* list of modern suites needs only READ. ALTER buys RSA with SHA-1. */
PERMIT RST.CIPHER.** CLASS($FLAM) ID(*) ACC(READ)
/* Substituting the trust anchor. Checked whether the value comes from the */
/* CABUNDLE parameter of store.s3()/store.azure() or from FL_CA_BUNDLE -- */
/* the act is the same act and the source does not change it. */
/* UPDATE and not ALTER: on z/OS a key ring is required for TLS at all, so */
/* ALTER here would make the secure path the harder one. */
PERMIT RST.CABUNDLE.** CLASS($FLAM) ID(*) ACC(UPDATE)
/* The backend trace. FL_HWTHTTP_VERBOSE at level 2 (UNREDACTED) writes the */
/* Authorization header into the trace -- an S3 SigV4 signature, and for */
/* Azure or anything reached through CDA the account key itself -- so it */
/* lands in a data set or in the job spool that operations can read. */
/* Level 1 redacts the header but still records endpoints, the certificate */
/* chain and timings, and FL_HWTHTTP_SSLTRACE names the key ring. */
/* */
/* READ a redacted trace, and the System SSL trace */
/* CONTROL UNREDACTED -- writing a credential out */
/* */
/* A refusal DOWNGRADES and never fails the run: UNREDACTED without CONTROL */
/* falls back to the redacted trace, and that to no trace. So a site with */
/* no profile keeps working and simply gets no trace. */
/* Grant CONTROL temporarily and on one endpoint when IBM support asks for */
/* an unredacted trace, and take it back afterwards: */
/* PERMIT RST.TRACE.https://s3.example.com CLASS($FLAM) */
/* ID(support_user) ACC(CONTROL) */
PERMIT RST.TRACE.** CLASS($FLAM) ID(*) ACC(READ)
PERMIT RST.GET.** CLASS($FLAM) ID(*) ACC(READ)
PERMIT RST.HEAD.** CLASS($FLAM) ID(*) ACC(READ)
PERMIT RST.PUT.** CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT RST.POST.** CLASS($FLAM) ID(*) ACC(UPDATE)
PERMIT RST.DELETE.** CLASS($FLAM) ID(*) ACC(CONTROL)
/* NOTE: Azure OAuth2 and MSI auth modes require RST.CONNECT and */
/* RST.POST permits for the token acquisition endpoints: */
/* OAuth2: https://login.microsoftonline.com (RST.CONNECT + RST.POST) */
/* MSI: http://169.254.169.254 (RST.CONNECT + RST.GET) */
/* The RST.CONNECT.** and RST.POST.**/RST.GET.** wildcards above cover */
/* these endpoints. For restrictive environments add explicit permits: */
/* PERMIT RST.CONNECT.https://login.microsoftonline.com CLASS($FLAM) */
/* ID(flam_users) ACC(CONTROL) */
/* PERMIT RST.POST.https://login.microsoftonline.com CLASS($FLAM) */
/* ID(flam_users) ACC(UPDATE) */
/* PERMIT RST.CONNECT.http://169.254.169.254 CLASS($FLAM) */
/* ID(flam_users) ACC(CONTROL) */
SETROPTS CLASSACT($FLAM)
SETROPTS RACLIST($FLAM)
SETROPTS RACLIST($FLAM) REFRESH
First (in step 1 and 2) the general resource class $FLAM must be setup. The last step defines the rights for the certain entities. The recommendation defines all the policies and enable anti virus scanning only with the defined ClamAV daemon (DSN must be adjusted).
The entities for command execution or parameter string can include whitespace or one of the other 4 invalid characters '(),;'. If such an invalid character is encountered in an entity string, it is replaced by an underscore '_' before the SAF check. For example the PDS(E) 'HLQ.PO(HUGO)' results in the resource name 'FIOURL.HLQ.PO_HUGO_'. The global pre-write pre-processing command 'ls -l' results in the resource name 'CMD.PRE.WRITE.GLOBAL.ls_-l'.