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.
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.
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
| Command | Meaning |
|---|---|
LISTUSER / LU | Show user profile (segments: BASE, TSO, OMVS, …) |
LISTGRP / LG | Show group profile and members |
LISTDSD / LD | Show dataset profile and access list |
RLIST | Show general-resource profile |
ADDUSER / AU | Create a new userid |
ALTUSER / AU (alias) | Modify a userid |
DELUSER / DU | Delete a userid |
ADDGROUP / AG | Create a new group |
CONNECT / CO | Add a user to a group |
REMOVE / RE | Remove a user from a group |
ADDSD / AD | Add a dataset profile |
ALTDSD / AD (alias) | Alter a dataset profile |
DELDSD / DD | Delete a dataset profile |
PERMIT / PE | Add or remove an access-list entry |
RDEFINE / RDEF | Define a general-resource profile |
RALTER / RALT | Alter a general-resource profile |
RDELETE / RDEL | Delete a general-resource profile |
SETROPTS / SETR | System-wide RACF options |
SEARCH / SR | Find 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.
(* 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:
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.
(* 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.
(* 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:
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.
(* 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:
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.
| Pattern | Matches |
|---|---|
% | 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) |
&RACUID | Replaced with the requesting userid at access time |
(* 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.
(* 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.
SETROPTS GENERIC(DATASET) REFRESH
Output:
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(...).
(* 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:
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
| Class | What it protects |
|---|---|
FACILITY | BPX.* USS hooks, IRR.* RACROUTE, STGADMIN.* storage admin |
OPERCMDS | MVS operator commands (MVS.START.*, JES2.MODIFY.*) |
TSOAUTH | TSO authorisations (OPER, MOUNT, ACCT) |
PROGRAM | Load modules — guards PADS and program-controlled environments |
JESJOBS | Submit, cancel, modify by job name |
JESSPOOL | Spool dataset access |
SURROGAT | Submit jobs under another userid (alice.SUBMIT) |
UNIXPRIV | Granular USS privileges (SUPERUSER.FILESYS.*) |
DSNR | DB2 subsystem access |
TCICSTRN / GCICSTRN | CICS transactions and groups |
XFACILIT | Long-name FACILITY (over 39 chars) |
STARTED | Started-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.
(* 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:
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.
(* Display every system option *)
SETROPTS LIST
Output:
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
(* 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.
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)
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.
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.
(* 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:
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:
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.
(* 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:
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.
(* 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:
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 output | Meaning |
|---|---|
EVENT | The RACF event code (ACCESS, ADDSD, PERMIT, LOGON, …) |
EVENT QUAL | SUCCESS, INSAUTH, VIOL, WARNED, FAILED |
USER | Userid that did the action |
JOBID | JES job ID (batch) or TSU* (TSO) |
RESOURCE | Dataset or general-resource name |
INTENT / ALLOWED | Requested vs granted access level |
PROFILE | Profile that matched (or **NOPROF** if none) |
Common pitfalls
- Forgetting
SETROPTS … REFRESHafter a generic change —PERMITruns cleanly, the user still gets denied. After any change to a generic DATASET profile runSETROPTS GENERIC(DATASET) REFRESH; for a RACLISTed class runSETROPTS RACLIST(class) REFRESH. - Discrete vs generic —
LISTDSD DATASET('ALICE.PROD')matches a discrete profile first; the matching generic is shown with(G). AddGENERICto force a generic-only view, otherwise you may be staring at a stale discrete profile no one cleaned up. UACC(READ)on a dataset profile is world-readable — universal access applies to every authenticated user, not just the access list. UseUACC(NONE)and grant explicitly viaPERMIT. TheSEARCH CLASS(DATASET) UACC(READ)query above is the audit-friendly way to find profiles that violate this.- PADS and the PROGRAM class — once
WHEN(PROGRAM)is active, datasets withWHEN(PROGRAM(name))permits are only accessible while running under that program. A user with no other access will getICH408Iviolations until they go through the controlled load module. *vs**in generic profiles —ALICE.*matchesALICE.Xbut notALICE.X.Y; you needALICE.**for that. The most common mistake when migrating from one system to another.- Password phrases require ICHPWX11 — the
PHRASE(...)keyword is rejected unless the password-phrase exit is installed andSETROPTS PASSWORD(RULE…)permits it. CheckSETROPTS LISTforPHRASE PROCESSING. SPECIALis not the same asOPERATIONS—SPECIALlets you administer RACF;OPERATIONSbypasses RACF dataset checks (for storage management). Granting OPERATIONS by mistake gives a user implicit ALTER on every dataset profile.- 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.
UID(0)in OMVS — any USS user with UID 0 is root and bypasses POSIX permissions. UseUNIXPRIVprofiles (SUPERUSER.FILESYS.*,SUPERUSER.PROCESS.*) for granular privilege instead.SEARCHwith noCLASS()defaults to DATASET — aSEARCH MASK(SYS1)against a non-DATASET class quietly returns nothing matching; always includeCLASS(...).
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.
(* 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:
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.
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:
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.
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.
(* 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).
(* 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.
(* 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.
//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:
IFA020I RECORDS READ FROM SMFIN 28714
IFA020I RECORDS WRITTEN TO ADUPRINT 1184
IRR67100I IRRADU00 PROCESSING COMPLETE. RC=0