cheat sheet

RACF

TSO commands for RACF user, group, dataset, and general-resource profile administration on z/OS.

RACF — z/OS Security Administration

What it is

RACF (Resource Access Control Facility) is IBM's security manager for z/OS — the System Authorization Facility (SAF) implementation that authenticates users at logon, authorises every dataset open and program call, and writes an SMF audit trail of access decisions. Profiles live in the RACF database and are administered through a small family of TSO commands: ADDUSER/ALTUSER for accounts, PERMIT for the access lists, RDEFINE for general-resource profiles, and SETROPTS for system-wide options. Reach for RACF on z/OS systems; the equivalent stacks ACF2 and Top Secret use different command syntax but cover the same SAF surface.

Install

RACF is shipped with z/OS as a base element; there is nothing to install. The TSO commands are available on every system where RACF is the active SAF backend. Use TSO option 6 (or =6 from anywhere in ISPF) to drop into a command line, or paste them into an ISPF Edit session and prefix with TSO to execute one ad-hoc.

text
TSO LU ALICE

Output: (none — exits 0 on success)

Syntax

Every RACF command is a TSO command word followed by positional and keyword operands; class and profile names are quoted with single quotes when they contain mixed case or special characters. Continuation across lines uses a trailing + or -; + strips leading blanks on the next line, - preserves them verbatim.

text
COMMAND  profile-name  KEYWORD(value)  KEYWORD(value)
COMMAND  profile-name  KEYWORD(value1 value2)         (* list values *)
COMMAND  profile-name  KEYWORD(value) -               (* continued line *)
                       KEYWORD(value)

Output: (none — exits 0 on success)

Essential commands

CommandMeaning
LISTUSER / LUShow user profile (segments: BASE, TSO, OMVS, …)
LISTGRP / LGShow group profile and members
LISTDSD / LDShow dataset profile and access list
RLISTShow general-resource profile
ADDUSER / AUCreate a new userid
ALTUSER / AU (alias)Modify a userid
DELUSER / DUDelete a userid
ADDGROUP / AGCreate a new group
CONNECT / COAdd a user to a group
REMOVE / RERemove a user from a group
ADDSD / ADAdd a dataset profile
ALTDSD / AD (alias)Alter a dataset profile
DELDSD / DDDelete a dataset profile
PERMIT / PEAdd or remove an access-list entry
RDEFINE / RDEFDefine a general-resource profile
RALTER / RALTAlter a general-resource profile
RDELETE / RDELDelete a general-resource profile
SETROPTS / SETRSystem-wide RACF options
SEARCH / SRFind profiles matching a mask or filter

Users and groups

User and group profiles are the foundation of RACF — every authenticated session runs under one user, and every user is connected to one default group plus optional additional groups. Each profile carries a stack of segments (BASE for the common attributes, TSO for the logon parameters, OMVS for the USS uid/gid/home, plus DFP, WORKATTR, NETVIEW, and others), and segments are added or queried independently with the BASE/TSO/OMVS keywords.

text
(* List a user with all segments *)
LISTUSER ALICE TSO OMVS WORKATTR

(* Just the BASE segment *)
LU ALICE

(* List everything in shorthand *)
LU ALICE ALL

Output:

text
USER=ALICE  NAME=ALICE DEV  OWNER=SECADM   CREATED=24.145
 DEFAULT-GROUP=USERS  PASSDATE=26.145  PASS-INTERVAL= 60 PHRASEDATE=N/A
 ATTRIBUTES=NONE
 REVOKE DATE=NONE   RESUME DATE=NONE
 LAST-ACCESS=26.144/10:32:11
 CLASS AUTHORIZATIONS=NONE
 NO-INSTALLATION-DATA
 NO-MODEL-NAME
 LOGON ALLOWED   (DAYS)          (TIME)
 ---------------------------------------------
 ANYDAY                          ANYTIME
  GROUP=USERS      AUTH=USE      CONNECT-OWNER=SECADM   CONNECT-DATE=24.145
    CONNECTS=  142  UACC=NONE     LAST-CONNECT=26.144/10:32:11
    CONNECT ATTRIBUTES=NONE
    REVOKE DATE=NONE   RESUME DATE=NONE

TSO INFORMATION
---------------
 ACCTNUM= ACCT#
 PROC= IKJACCNT
 SIZE= 00065536
 MAXSIZE= 00000000
 USERDATA= 0000

OMVS INFORMATION
----------------
 UID= 0000000501
 HOME= /home/alice
 PROGRAM= /bin/sh

Creating and modifying users

ADDUSER creates the userid and seeds whichever segments are listed; ALTUSER changes an existing user. The BASE attributes (SPECIAL, OPERATIONS, AUDITOR) grant system-wide privilege and should be limited to the security team. Default group, owner, and password are required for a usable account — the password is set to a temporary value here and forced to expire so the user must change it at first logon.

text
(* New user with TSO and OMVS *)
ADDUSER ALICE -
   NAME('Alice Dev')                    -
   OWNER(SECADM)                        -
   DFLTGRP(USERS)                       -
   PASSWORD(WELC0M3)                    -
   TSO(ACCTNUM(ACCT#) PROC(IKJACCNT)    -
       SIZE(65536))                     -
   OMVS(UID(501) HOME('/home/alice')   -
        PROGRAM('/bin/sh'))

(* Force password change on next logon *)
ALTUSER ALICE PASSWORD(WELC0M3) EXPIRED

(* Grant SPECIAL — system-wide RACF authority *)
ALTUSER ALICE SPECIAL

(* Revoke and resume *)
ALTUSER ALICE REVOKE
ALTUSER ALICE RESUME

(* Set a password phrase *)
ALTUSER ALICE PHRASE('correct horse battery staple') EXPIRED

Output: (none — exits 0 on success)

Groups and connects

Groups own dataset profiles, scope administrative authority, and gather users for PERMIT ID(group) access. A user inherits the access of every group they are CONNECTed to; the default group is the one assumed when RACF=NONE is omitted from the JOB card.

text
(* New group *)
ADDGROUP DEVTEAM OWNER(SECADM) SUPGROUP(USERS)

(* Connect a user with USE authority *)
CONNECT ALICE GROUP(DEVTEAM) AUTHORITY(USE)

(* Higher authorities: CREATE, CONNECT, JOIN *)
CONNECT ALICE GROUP(DEVTEAM) AUTHORITY(CONNECT)

(* Remove from a group *)
REMOVE ALICE GROUP(DEVTEAM)

(* List a group and its connected users *)
LISTGRP DEVTEAM

Output:

text
INFORMATION FOR GROUP DEVTEAM
   SUPERIOR GROUP=USERS  OWNER=SECADM    CREATED=26.040
   NO INSTALLATION DATA
   NO MODEL DATA SET
   TERMUACC
   NO SUBGROUPS
   USER(S)=      ACCESS=   ACCESS COUNT=   UNIVERSAL ACCESS=
     ALICE        USE          14              NONE
     BOB          CREATE        2              NONE

Dataset profiles

Dataset profiles control who can read, write, or delete MVS datasets and are matched against the dataset name at OPEN time. The first qualifier of the dataset name (the HLQ) must match either a userid or a group; access is granted by the most specific profile that covers the dataset. Profiles are either discrete (cover one exact dataset) or generic (cover a pattern with */%/**) — generics are the workhorse and are the only kind you should be writing for a real environment.

text
(* Discrete profile — exact match only *)
ADDSD 'ALICE.PAYROLL.MASTER' UACC(NONE) OWNER(ALICE)

(* Generic — covers ALICE.PAYROLL.anything (single qualifier) *)
ADDSD 'ALICE.PAYROLL.*' UACC(NONE) OWNER(ALICE) GENERIC

(* Multi-qualifier generic *)
ADDSD 'ALICE.PAYROLL.**' UACC(NONE) OWNER(ALICE) GENERIC

(* List the profile and its access list *)
LISTDSD DATASET('ALICE.PAYROLL.**') ALL GENERIC

Output:

text
INFORMATION FOR DATASET ALICE.PAYROLL.** (G)

LEVEL  OWNER      UNIVERSAL ACCESS   WARNING   ERASE
 00    ALICE          NONE             NO       NO

AUDITING
--------
FAILURES(READ)

NOTIFY
------
NO USER TO BE NOTIFIED

YOUR ACCESS  CREATION GROUP   DATASET TYPE
-----------  --------------   ------------
   ALTER         DEVTEAM         NON-VSAM

NO INSTALLATION DATA

ID            ACCESS   ACCESS COUNT
--------      ------   ------------
DEVTEAM         UPDATE      42
ALICE           ALTER       91

Profile rules and wildcards

Generic profiles use a small pattern language; understanding it is the difference between locking down the right datasets and locking the wrong ones. The pattern is matched against the fully qualified dataset name at access time, never against another profile.

PatternMatches
%Exactly one character within a single qualifier
*Zero or more characters within a single qualifier (does not cross a .)
**Zero or more whole qualifiers (only at the start, end, or between two qualifiers)
&RACUIDReplaced with the requesting userid at access time
text
(* All datasets owned by ALICE *)
ADDSD 'ALICE.**' UACC(NONE) GENERIC

(* Any 8-char third qualifier *)
ADDSD 'ALICE.PROJ.%%%%%%%%' UACC(NONE) GENERIC

(* Any qualifier ending in .LOAD anywhere in the name *)
ADDSD 'ALICE.**.LOAD' UACC(NONE) GENERIC

(* Substring within one qualifier *)
ADDSD 'ALICE.JCL*.PROD' UACC(NONE) GENERIC

Output: (none — exits 0 on success)

PERMIT and access levels

PERMIT adds (or with DELETE removes) an entry on a profile's access list. The level is one of NONE, EXECUTE (load-library only), READ, UPDATE, CONTROL (VSAM only), ALTER. The most permissive matching entry wins; a user matching multiple groups gets the union of all their accesses.

text
(* Grant a user READ *)
PERMIT 'ALICE.PAYROLL.**' ID(BOB) ACCESS(READ) GENERIC

(* Grant a group UPDATE *)
PERMIT 'ALICE.PAYROLL.**' ID(DEVTEAM) ACCESS(UPDATE) GENERIC

(* Revoke *)
PERMIT 'ALICE.PAYROLL.**' ID(BOB) DELETE GENERIC

(* Reset (delete) the entire access list *)
PERMIT 'ALICE.PAYROLL.**' RESET GENERIC

(* List the result *)
LD DA('ALICE.PAYROLL.**') AUTHUSER GENERIC

Output: (none — exits 0 on success)

REFRESH after generic changes

Generic profile lookups are cached in storage. After ADDSD, ALTDSD, DELDSD, or PERMIT against a generic profile, a SETROPTS GENERIC(DATASET) REFRESH is required for the change to take effect across the system.

text
SETROPTS GENERIC(DATASET) REFRESH

Output:

text
ICH14063I SETROPTS COMMAND COMPLETE.

General-resource profiles

Anything that is not a dataset (TSO commands, JES output classes, DB2 plans, USS facilities, CICS transactions, FTP commands, RRSF nodes, …) is protected by a general resource in one of dozens of classes such as FACILITY, OPERCMDS, TSOAUTH, PROGRAM, JESJOBS, SURROGAT, UNIXPRIV. The profile lives in the class, the protected name lives in the profile, and access is granted exactly the same way: PERMIT name CLASS(class) ID(...) ACCESS(...).

text
(* Create a FACILITY profile guarding a USS function *)
RDEFINE FACILITY BPX.SUPERUSER UACC(NONE) OWNER(SECADM)

(* Grant a user *)
PERMIT BPX.SUPERUSER CLASS(FACILITY) ID(ALICE) ACCESS(READ)

(* Show the profile *)
RLIST FACILITY BPX.SUPERUSER AUTHUSER

Output:

text
CLASS      NAME
-----      ----
FACILITY   BPX.SUPERUSER

LEVEL  OWNER     UNIVERSAL ACCESS  YOUR ACCESS  WARNING
 00    SECADM         NONE            READ        NO

USER      ACCESS   ACCESS COUNT
----      ------   ------------
ALICE     READ          17

Common general-resource classes

ClassWhat it protects
FACILITYBPX.* USS hooks, IRR.* RACROUTE, STGADMIN.* storage admin
OPERCMDSMVS operator commands (MVS.START.*, JES2.MODIFY.*)
TSOAUTHTSO authorisations (OPER, MOUNT, ACCT)
PROGRAMLoad modules — guards PADS and program-controlled environments
JESJOBSSubmit, cancel, modify by job name
JESSPOOLSpool dataset access
SURROGATSubmit jobs under another userid (alice.SUBMIT)
UNIXPRIVGranular USS privileges (SUPERUSER.FILESYS.*)
DSNRDB2 subsystem access
TCICSTRN / GCICSTRNCICS transactions and groups
XFACILITLong-name FACILITY (over 39 chars)
STARTEDStarted-task associations

RACLISTed classes

Classes that are RACLISTed are kept in storage for fast lookup; changes to them need a class-specific refresh instead of the DATASET refresh.

text
(* List which classes are RACLISTed *)
SETROPTS LIST | FIND 'RACLIST'

(* Refresh a single class *)
SETROPTS RACLIST(FACILITY) REFRESH

(* Refresh several at once *)
SETROPTS RACLIST(FACILITY OPERCMDS UNIXPRIV) REFRESH

Output:

text
ICH14063I SETROPTS COMMAND COMPLETE.

SETROPTS — system-wide options

SETROPTS is the one command that configures RACF as a whole — which classes are active, which are generic-enabled, which are RACLISTed, what the password rules are, and which kinds of events generate SMF records. Only users with the SPECIAL attribute can change these; AUDITOR can read them and change audit-related ones.

text
(* Display every system option *)
SETROPTS LIST

Output:

text
ATTRIBUTES = INITSTATS WHEN(PROGRAM) NOEGN
STATISTICS = NONE
ACTIVE CLASSES = DATASET USER GROUP DASDVOL TAPEVOL TERMINAL APPL JESJOBS ...
GENERIC PROFILE CLASSES = DATASET FACILITY OPERCMDS ...
GENLIST CLASSES = NONE
GLOBAL CHECKING CLASSES = NONE
SETROPTS AUDIT CLASSES = USER GROUP DATASET
LOGOPTIONS:
  ALWAYS  = NONE
  NEVER   = NONE
  SUCCESSES = NONE
  FAILURES = DATASET FACILITY OPERCMDS
DEFAULT - DEFAULT - DEFAULT
PASSWORD PROCESSING OPTIONS:
  PASSWORD CHANGE INTERVAL IS 60 DAYS.
  PASSWORD MINIMUM CHANGE INTERVAL IS 1 DAYS.
  MIXED CASE PASSWORD SUPPORT IS IN EFFECT
  6 REVISIONS TO BE REMEMBERED
  ...

Common SETROPTS commands

text
(* Activate a class and turn on generic profiles for it *)
SETROPTS CLASSACT(FACILITY) GENERIC(FACILITY)

(* RACLIST a class for in-storage lookup *)
SETROPTS RACLIST(FACILITY)

(* Refresh after profile changes *)
SETROPTS GENERIC(DATASET) REFRESH
SETROPTS RACLIST(FACILITY) REFRESH
SETROPTS WHEN(PROGRAM) REFRESH

(* Password rules — note: MIXEDALL requires content-keyword setup *)
SETROPTS PASSWORD(INTERVAL(60) HISTORY(6) REVOKE(5) MINCHANGE(1) +
                  RULE1(LENGTH(8:12) ALPHA(1,8) NUMERIC(2,7) +
                        MIXEDCONSONANT(1) NOVOWEL))

(* MIXEDALL — modern rule, any-position alphanumeric + special chars *)
SETROPTS PASSWORD(MIXEDCASE) PASSWORD(SPECIALCHARS)
SETROPTS PASSWORD(RULE2(LENGTH(8:12) MIXEDALL(1:12)))

(* KDFAES — stronger password hashing (also disables PWXPWHST field) *)
SETROPTS PASSWORD(ALGORITHM(KDFAES))

(* Audit *)
SETROPTS AUDIT(DATASET USER GROUP)
SETROPTS LOGOPTIONS(FAILURES(DATASET FACILITY))
SETROPTS LOGOPTIONS(SUCCESSES(OPERCMDS))
SETROPTS OPERAUDIT

(* Erase-on-scratch (DoD requirement) *)
SETROPTS ERASE(ALL)

(* Display only the password section *)
SETROPTS LIST PASSWORD

Output: (none — exits 0 on success)

What's new — z/OS 3.2 and recent APARs

z/OS 3.2 (GA 30 September 2025) and recent APAR rollups deliver several RACF capabilities worth knowing about. None of them change the day-to-day TSO syntax, but each unlocks new policy options.

ALTUSER ... CONTAIN — quarantine a userid (z/OS 3.2)

Until 3.2 the strongest immediate action against a compromised account was ALTUSER ... REVOKE, which prevents new logons but lets currently-active sessions continue accessing RACF-protected resources. z/OS 3.2 adds CONTAIN (and NOCONTAIN) — when applied, the userid is revoked and in-flight access decisions for that userid start denying immediately, so an attacker who is already authenticated cannot continue to open new datasets. Pair with UAUDIT for a complete forensic trail. The companion product IBM Threat Detection for z/OS (TDz) can trigger CONTAIN automatically when an anomaly score crosses a threshold.

text
ALTUSER EMRG01 CONTAIN UAUDIT          (* quarantine — denies even active sessions *)
ALTUSER EMRG01 NOCONTAIN               (* release the quarantine *)
LU EMRG01                              (* CONTAIN status shows in BASE segment *)

Output: (LU excerpt)

text
USER=EMRG01  NAME=EMERGENCY FIX  OWNER=SECADM
 ATTRIBUTES=SPECIAL CONTAIN
 REVOKE DATE=NONE   RESUME DATE=NONE
 UAUDIT=YES

Digital certificate enhancements (z/OS 3.2)

z/OS 3.2 RACF adds multi-alternate-name support to RACDCERT and certificate validation — a single certificate can carry multiple Subject Alternative Names (SANs) for different hostnames or service accounts. SHA-384 and SHA-512 signing algorithms are accepted on certificate generation, replacing the SHA-256 ceiling of earlier releases. Use these when partner systems require certificates that comply with current CA/Browser Forum baselines or with FIPS 140-3 cipher suites.

text
RACDCERT GENCERT(myhost.example.com) -
   ID(WEBSRV1) -
   SUBJECTSDN(CN('myhost.example.com')) -
   ALTNAME(IP('10.20.30.5')                  -
           DOMAIN('myhost.example.com')      -
           DOMAIN('api.example.com')         -
           DOMAIN('admin.example.com')) -
   SIZE(4096) SIGNWITH(CERTAUTH LABEL('SiteCA')) -
   WITHLABEL('webcert') -
   HASH(SHA512)

Output: (none — exits 0 on success; verify with RACDCERT LIST(LABEL('webcert')) ID(WEBSRV1))

Multi-Factor Authentication and RACF Identity Tokens

IBM Z Multi-Factor Authentication (IBM Z MFA, product 5655-MA1, current release 2.2) integrates with RACF for layered authentication. Modern shops typically run compound in-band: the user types their RACF password or passphrase followed by an OTP from IBM Cloud Identity Verify (CIV), Yubikey, RSA SecurID, or TOTP. Configure factors with MFADEF general-resource profiles and bind them to a userid with ALTUSER ... MFA(...). APAR OA55926 introduced RACF Identity Tokens — short-lived JWTs returned by RACROUTE REQUEST=VERIFY that callers can replay across subsequent verify calls so the entire authentication sequence is recognised as one transaction.

text
(* Activate the MFADEF class and bind a TOTP factor to a user *)
SETROPTS CLASSACT(MFADEF) GENERIC(MFADEF) RACLIST(MFADEF)
RDEFINE MFADEF FACTOR.AZFTOTP1 UACC(NONE)
ALTUSER ALICE MFA(FACTOR(AZFTOTP1) ACTIVE TAGS(TIMEOUT:60))

(* Tag the userid as "MFA required" — userid cannot log on with password alone *)
ALTUSER ALICE MFA(PWFALLBACK(NO))

(* Verify *)
LU ALICE MFA

Output:

text
MFA INFORMATION
---------------
PWFALLBACK = NO
FACTOR = AZFTOTP1  ACTIVE = YES  TAGS = TIMEOUT:60
LAST FACTOR USED = AZFTOTP1

Cloud Identity Verify and SAML/JWT bridge

IBM Cloud Identity Verify (CIV) can issue OTPs in-band, and recent APARs extend the bridge so an external SAML/JWT identity provider can be the primary authenticator with RACF acting as the resource manager. The mapping is held in the IDIDMAP class — a SAML name-ID or JWT subject is mapped to a RACF userid:

text
RDEFINE IDIDMAP SAML.alice@example.com APPLDATA('ALICE') UACC(NONE)
SETROPTS RACLIST(IDIDMAP) REFRESH

Output: (none — exits 0 on success)

SEARCH — find profiles

SEARCH queries the RACF database for profiles matching a name mask or by attribute. It is the right tool for inventorying everything a user can touch, finding profiles with broad UACC, or pulling a working list before a migration.

text
(* All dataset profiles starting with ALICE *)
SEARCH CLASS(DATASET) MASK(ALICE)

(* Same with a filter character (% any char in qualifier, * any qualifier) *)
SEARCH CLASS(DATASET) FILTER(ALICE.*.PROD.**)

(* Profiles owned by ALICE *)
SEARCH USER(ALICE) CLASS(DATASET)

(* Profiles with UACC ≥ READ — the audit favourite *)
SEARCH CLASS(DATASET) UACC(READ)

(* Profiles a user has explicit access to *)
SEARCH USER(BOB) CLASS(DATASET) ACCESS(UPDATE)

(* Profiles in a general-resource class *)
SEARCH CLASS(FACILITY) MASK(BPX)

(* Pipe output as CLIST input — generate commands *)
SEARCH CLASS(DATASET) MASK(ALICE) -
       CLIST('PERMIT ' ' ID(BOB) ACCESS(READ) GENERIC')
EX 'ALICE.EXEC.RACF.CLIST'

Output:

text
ALICE
ALICE.JCL.**             (G)
ALICE.PAYROLL.**         (G)
ALICE.SOURCE.**          (G)
ALICE.LOAD.**            (G)

Auditing and SMF records

RACF writes audit events to SMF — type 80 records cover RACF command activity and access decisions, type 81 records cover the RACF initialisation at IPL, and type 83 records cover R_auditx events from Db2, RRSF, and other subsystems. The combination of SETROPTS AUDIT, SETROPTS LOGOPTIONS, and per-profile AUDIT(...) keywords decides which events get cut.

text
(* Audit a single dataset profile aggressively *)
ALTDSD 'ALICE.PAYROLL.**' AUDIT(SUCCESS(UPDATE) FAILURES(READ)) -
       GENERIC NOTIFY(ALICE)

(* Audit a FACILITY profile *)
RALTER FACILITY BPX.SUPERUSER AUDIT(ALL(READ))

(* System-wide: audit all failures in DATASET and FACILITY *)
SETROPTS LOGOPTIONS(FAILURES(DATASET FACILITY))

(* System-wide: audit successful TSO logons *)
SETROPTS LOGOPTIONS(SUCCESSES(TSOAUTH))

(* Generate a quick report from SMF using IRRADU00 *)
//RACFRPT  EXEC PGM=IFASMFDP
//SYSPRINT DD SYSOUT=*
//ADUPRINT DD SYSOUT=*
//OUTDD    DD DSN=ALICE.IRRADU00.OUT,DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(5,5)),DCB=(RECFM=VB,LRECL=8192,BLKSIZE=0)
//SMFIN    DD DSN=SYS1.MAN1,DISP=SHR
//SMFOUT   DD DUMMY
//SYSIN    DD *
   INDD(SMFIN,OPTIONS(DUMP))
   OUTDD(SMFOUT,TYPE(000:255))
   USER2(IRRADU00)
   USER3(IRRADU86)
/*

Output:

text
IFA010I  SMF DUMP PARAMETERS
         INDD(SMFIN,OPTIONS(DUMP))
         OUTDD(SMFOUT,TYPE(000:255))
         USER2(IRRADU00) -- RECORDS EXTRACTED
         USER3(IRRADU86)
IFA020I  RECORDS READ FROM SMFIN              123456
IFA020I  RECORDS WRITTEN TO ADUPRINT           4218
IRR67100I  IRRADU00 PROCESSING COMPLETE.  RC=0

Useful audit fields

Field in IRRADU00 outputMeaning
EVENTThe RACF event code (ACCESS, ADDSD, PERMIT, LOGON, …)
EVENT QUALSUCCESS, INSAUTH, VIOL, WARNED, FAILED
USERUserid that did the action
JOBIDJES job ID (batch) or TSU* (TSO)
RESOURCEDataset or general-resource name
INTENT / ALLOWEDRequested vs granted access level
PROFILEProfile that matched (or **NOPROF** if none)

Common pitfalls

  1. Forgetting SETROPTS … REFRESH after a generic changePERMIT runs cleanly, the user still gets denied. After any change to a generic DATASET profile run SETROPTS GENERIC(DATASET) REFRESH; for a RACLISTed class run SETROPTS RACLIST(class) REFRESH.
  2. Discrete vs genericLISTDSD DATASET('ALICE.PROD') matches a discrete profile first; the matching generic is shown with (G). Add GENERIC to force a generic-only view, otherwise you may be staring at a stale discrete profile no one cleaned up.
  3. UACC(READ) on a dataset profile is world-readable — universal access applies to every authenticated user, not just the access list. Use UACC(NONE) and grant explicitly via PERMIT. The SEARCH CLASS(DATASET) UACC(READ) query above is the audit-friendly way to find profiles that violate this.
  4. PADS and the PROGRAM class — once WHEN(PROGRAM) is active, datasets with WHEN(PROGRAM(name)) permits are only accessible while running under that program. A user with no other access will get ICH408I violations until they go through the controlled load module.
  5. * vs ** in generic profilesALICE.* matches ALICE.X but not ALICE.X.Y; you need ALICE.** for that. The most common mistake when migrating from one system to another.
  6. Password phrases require ICHPWX11 — the PHRASE(...) keyword is rejected unless the password-phrase exit is installed and SETROPTS PASSWORD(RULE…) permits it. Check SETROPTS LIST for PHRASE PROCESSING.
  7. SPECIAL is not the same as OPERATIONSSPECIAL lets you administer RACF; OPERATIONS bypasses RACF dataset checks (for storage management). Granting OPERATIONS by mistake gives a user implicit ALTER on every dataset profile.
  8. Group-OWNER vs profile-OWNER — the OWNER of a profile can administer it; the OWNER of a group can administer the group's members. Don't conflate them or you'll grant the wrong scope.
  9. UID(0) in OMVS — any USS user with UID 0 is root and bypasses POSIX permissions. Use UNIXPRIV profiles (SUPERUSER.FILESYS.*, SUPERUSER.PROCESS.*) for granular privilege instead.
  10. SEARCH with no CLASS() defaults to DATASET — a SEARCH MASK(SYS1) against a non-DATASET class quietly returns nothing matching; always include CLASS(...).

Real-world recipes

Grant a TSO user UPDATE access to a new dataset prefix

A developer needs to write to ALICE.PROD.PAYROLL.**. The profile may not exist yet; you also want failure logging and a notification on access violations.

text
(* 1. Create the generic profile if it doesn't exist *)
ADDSD 'ALICE.PROD.PAYROLL.**' UACC(NONE) OWNER(SECADM) -
   NOTIFY(SECADM) GENERIC AUDIT(FAILURES(READ))

(* 2. PERMIT *)
PERMIT 'ALICE.PROD.PAYROLL.**' ID(BOB) ACCESS(UPDATE) GENERIC

(* 3. Refresh *)
SETROPTS GENERIC(DATASET) REFRESH

(* 4. Verify *)
LISTDSD DATASET('ALICE.PROD.PAYROLL.**') ALL GENERIC

Output:

text
ICH06011I RACF DATABASE BACKUP DATA SET IS NOT ACTIVE.
ICH14063I SETROPTS COMMAND COMPLETE.
INFORMATION FOR DATASET ALICE.PROD.PAYROLL.** (G)
...
   ID       ACCESS   ACCESS COUNT
   BOB      UPDATE      0

Onboard a new developer

Create the userid, attach the TSO and OMVS segments, connect to the development group, and confirm the result.

text
ADDUSER CAROL -
   NAME('Carol Dev')                 -
   OWNER(SECADM)                    -
   DFLTGRP(USERS)                   -
   PASSWORD(C4r0lDev)               -
   TSO(ACCTNUM(ACCT#) PROC(IKJACCNT) SIZE(65536)) -
   OMVS(UID(503) HOME('/home/carol') PROGRAM('/bin/sh'))

ALTUSER CAROL PASSWORD(C4r0lDev) EXPIRED

CONNECT CAROL GROUP(DEVTEAM) AUTHORITY(USE)

LISTUSER CAROL TSO OMVS

Output:

text
USER=CAROL  NAME=CAROL DEV  OWNER=SECADM   CREATED=26.145
 DEFAULT-GROUP=USERS
 ATTRIBUTES=NONE
 ...
 GROUP=DEVTEAM    AUTH=USE
 ...
TSO INFORMATION  ACCTNUM=ACCT#  PROC=IKJACCNT  SIZE=65536
OMVS INFORMATION  UID=503  HOME=/home/carol  PROGRAM=/bin/sh

Lock down an emergency-fix userid

A break-glass account should be REVOKEd by default, RESUMEd on demand, force password-change every login, and be audited heavily.

text
ADDUSER EMRG01 NAME('Emergency Fix') OWNER(SECADM) -
   DFLTGRP(SECGRP) PASSWORD(Br3@kGl@s) -
   TSO(ACCTNUM(SEC#) PROC(IKJACCNT))

ALTUSER EMRG01 SPECIAL                 (* tightly held privilege *)
ALTUSER EMRG01 REVOKE                  (* default locked *)

(* Audit every action *)
SETROPTS LOGOPTIONS(SUCCESSES(USER))

(* Audit any time the userid does anything *)
ALTUSER EMRG01 UAUDIT

Output: (none — exits 0 on success)

Convert from UACC(READ) to explicit READ

You discovered (via the audit SEARCH above) that a dataset has world-read enabled and need to lock it down without breaking current consumers.

text
(* 1. Find who has been using it (last 30 days, SMF reduction is offline) *)
SEARCH CLASS(DATASET) MASK(SHARED.REPORTS) ACCESS(UPDATE)

(* 2. Add an explicit READ for the group that legitimately needs it *)
PERMIT 'SHARED.REPORTS.**' ID(REPORTRS) ACCESS(READ) GENERIC

(* 3. Lower UACC to NONE *)
ALTDSD 'SHARED.REPORTS.**' UACC(NONE) GENERIC

(* 4. Refresh *)
SETROPTS GENERIC(DATASET) REFRESH

(* 5. Watch for legitimate breakage for a week *)
ALTDSD 'SHARED.REPORTS.**' AUDIT(FAILURES(READ)) GENERIC NOTIFY(SECADM)

Output: (none — exits 0 on success)

Allow a user to submit jobs as another userid

The SURROGAT class controls whether one user (the surrogate) can submit a job that runs under another userid (the execution user).

text
(* Define the profile guarding job submission under userid PRODSVC *)
RDEFINE SURROGAT PRODSVC.SUBMIT UACC(NONE) OWNER(SECADM)

(* PERMIT the user *)
PERMIT PRODSVC.SUBMIT CLASS(SURROGAT) ID(ALICE) ACCESS(READ)

(* SURROGAT is RACLISTed in most shops *)
SETROPTS RACLIST(SURROGAT) REFRESH

(* ALICE can now code USER=PRODSVC on a JOB card *)
LD DA('ALICE.JCL.LIB(SURJOB)')

Output: (none — exits 0 on success)

Find every profile a leaving employee owns or can use

Before deleting the user account, transfer ownership and revoke explicit access.

text
(* Profiles they own *)
SEARCH USER(DAVE) CLASS(DATASET)

(* Profiles they appear on the access list of *)
SEARCH CLASS(DATASET) FILTER(**) USER(DAVE) ACCESS(READ:ALTER)

(* Generate transfer commands *)
SEARCH USER(DAVE) CLASS(DATASET) -
   CLIST('ALTDSD ' ' OWNER(SECADM) GENERIC')
EX 'DAVE.EXEC.RACF.CLIST'

(* Generate removal commands *)
SEARCH CLASS(DATASET) FILTER(**) USER(DAVE) -
   CLIST('PERMIT ' ' ID(DAVE) DELETE GENERIC')
EX 'DAVE.EXEC.RACF.CLIST'

(* Finally, delete the userid *)
DELUSER DAVE

Output: (none — exits 0 on success)

Build a one-shot SMF audit report

Pull yesterday's RACF events, format with IRRADU00, and dump to a sequential dataset for spreadsheet ingestion. Pair this with a Zowe files download to pull it to a laptop.

text
//ALICEJ01 JOB (ACCT),'AUDIT REPORT',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//RACFRPT  EXEC PGM=IFASMFDP
//SYSPRINT DD SYSOUT=*
//ADUPRINT DD SYSOUT=*
//OUTDD    DD DSN=ALICE.IRRADU00.YDAY,DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(5,5)),DCB=(RECFM=VB,LRECL=8192)
//SMFIN    DD DSN=SYS1.MAN1,DISP=SHR
//SMFOUT   DD DUMMY
//SYSIN    DD *
   INDD(SMFIN,OPTIONS(DUMP))
   OUTDD(SMFOUT,TYPE(080))
   USER2(IRRADU00)
   DATE(2026146,2026146)
/*

Output:

text
IFA020I  RECORDS READ FROM SMFIN              28714
IFA020I  RECORDS WRITTEN TO ADUPRINT           1184
IRR67100I  IRRADU00 PROCESSING COMPLETE.  RC=0

Sources