IBM i Security

Audit Restored IBM i Job Descriptions That Specify a User Profile

Use QSYS2.DISPLAY_JOURNAL to find and decode RJ audit entries for restored IBM i job descriptions that specify a user profile, assess post-restore authority exposure, and build a controlled recovery review.

IBM iIBM i SecuritySQLAudit JournalQAUDJRNDISPLAY_JOURNALRJ Audit EntryJob DescriptionRestoreDisaster Recovery

A restored IBM i job description can quietly reintroduce a named user profile into the way future jobs are started. The RJ audit entry identifies restored job descriptions whose USER parameter specifies a profile, including the current and previous profile values.

This matters after:

The central questions are:

Which job description was restored?
Which library received it?
What profile is now specified?
Was a different profile previously specified?
Who performed the restore?
Which job and program performed the work?
Does the restored configuration match the target environment?

The RJ audit entry

IBM defines audit entry type:

RJ

as restoring a job description that contains a user profile in its USER parameter.

The entry-specific data includes:

ENTRY_TYPE
JOB_DESCRIPTION_NAME
LIBRARY_NAME
OBJECT_TYPE
USER_NAME
ASP_NAME
ASP_NUMBER
PREVIOUS_USER_NAME

For ENTRY_TYPE, the value A means that a job description with a profile specified in USER was restored.

Important service limitation

IBM does not currently document a dedicated function named:

SYSTOOLS.AUDIT_JOURNAL_RJ

For RJ entries, use:

QSYS2.DISPLAY_JOURNAL
CPYAUDJRNE

DISPLAY_JOURNAL exposes the common audit metadata and raw ENTRY_DATA through SQL. CPYAUDJRNE can copy all RJ fields to a formatted outfile.

Find recent RJ entries

SELECT
    ENTRY_TIMESTAMP,
    SEQUENCE_NUMBER,
    JOURNAL_ENTRY_TYPE,
    USER_NAME AS RESTORE_RUN_BY,
    QUALIFIED_JOB_NAME,
    PROGRAM_LIBRARY,
    PROGRAM_NAME,
    RECEIVER_LIBRARY,
    RECEIVER_NAME,
    ENTRY_DATA
FROM TABLE(
    QSYS2.DISPLAY_JOURNAL(
        JOURNAL_LIBRARY => 'QSYS',
        JOURNAL_NAME => 'QAUDJRN',
        STARTING_RECEIVER_NAME => '*CURAVLCHN',
        JOURNAL_ENTRY_TYPES => 'RJ',
        STARTING_TIMESTAMP => CURRENT TIMESTAMP - 7 DAYS
    )
)
ORDER BY
    ENTRY_TIMESTAMP DESC,
    SEQUENCE_NUMBER DESC;

Decode the RJ entry-specific data

DISPLAY_JOURNAL returns ENTRY_DATA as a BLOB. In the RJ type-5 layout, the fields begin at these relative positions inside the entry-specific payload:

1      Entry type                 CHAR(1)
2      Job description name      CHAR(10)
12     Library name              CHAR(10)
22     Object type               CHAR(8)
30     Current user name         CHAR(10)
40     ASP name                  CHAR(10)
50     ASP number                CHAR(5)
55     Previous user name        CHAR(10)

Use SQL to decode the values:

WITH RJ_ENTRIES AS
(
    SELECT
        J.*,

        CAST(
            CAST(SUBSTRING(ENTRY_DATA, 1, 1)
                 AS CHAR(1) FOR BIT DATA)
            AS CHAR(1) CCSID 37
        ) AS RJ_ENTRY_TYPE,

        TRIM(CAST(
            CAST(SUBSTRING(ENTRY_DATA, 2, 10)
                 AS CHAR(10) FOR BIT DATA)
            AS CHAR(10) CCSID 37
        )) AS JOB_DESCRIPTION,

        TRIM(CAST(
            CAST(SUBSTRING(ENTRY_DATA, 12, 10)
                 AS CHAR(10) FOR BIT DATA)
            AS CHAR(10) CCSID 37
        )) AS JOB_DESCRIPTION_LIBRARY,

        TRIM(CAST(
            CAST(SUBSTRING(ENTRY_DATA, 22, 8)
                 AS CHAR(8) FOR BIT DATA)
            AS CHAR(8) CCSID 37
        )) AS RESTORED_OBJECT_TYPE,

        TRIM(CAST(
            CAST(SUBSTRING(ENTRY_DATA, 30, 10)
                 AS CHAR(10) FOR BIT DATA)
            AS CHAR(10) CCSID 37
        )) AS RESTORED_USER,

        TRIM(CAST(
            CAST(SUBSTRING(ENTRY_DATA, 40, 10)
                 AS CHAR(10) FOR BIT DATA)
            AS CHAR(10) CCSID 37
        )) AS ASP_NAME,

        TRIM(CAST(
            CAST(SUBSTRING(ENTRY_DATA, 50, 5)
                 AS CHAR(5) FOR BIT DATA)
            AS CHAR(5) CCSID 37
        )) AS ASP_NUMBER,

        TRIM(CAST(
            CAST(SUBSTRING(ENTRY_DATA, 55, 10)
                 AS CHAR(10) FOR BIT DATA)
            AS CHAR(10) CCSID 37
        )) AS PREVIOUS_USER

    FROM TABLE(
        QSYS2.DISPLAY_JOURNAL(
            JOURNAL_LIBRARY => 'QSYS',
            JOURNAL_NAME => 'QAUDJRN',
            STARTING_RECEIVER_NAME => '*CURAVLCHN',
            JOURNAL_ENTRY_TYPES => 'RJ',
            STARTING_TIMESTAMP => CURRENT TIMESTAMP - 30 DAYS
        )
    ) AS J
)
SELECT
    ENTRY_TIMESTAMP,
    USER_NAME AS RESTORE_RUN_BY,
    QUALIFIED_JOB_NAME,
    PROGRAM_LIBRARY,
    PROGRAM_NAME,
    JOB_DESCRIPTION_LIBRARY,
    JOB_DESCRIPTION,
    RESTORED_OBJECT_TYPE,
    PREVIOUS_USER,
    RESTORED_USER,
    ASP_NAME,
    ASP_NUMBER,
    RECEIVER_LIBRARY,
    RECEIVER_NAME,
    SEQUENCE_NUMBER
FROM RJ_ENTRIES
WHERE RJ_ENTRY_TYPE = 'A'
ORDER BY
    ENTRY_TIMESTAMP DESC,
    SEQUENCE_NUMBER DESC;

CCSID considerations

The example uses CCSID 37, a common North American EBCDIC CCSID. Use the CCSID appropriate for your system and audit data.

If values appear corrupted:

Find profile changes introduced by restore

The most important records are those where the restore changed the named profile:

SELECT
    ENTRY_TIMESTAMP,
    RESTORE_RUN_BY,
    QUALIFIED_JOB_NAME,
    JOB_DESCRIPTION_LIBRARY,
    JOB_DESCRIPTION,
    PREVIOUS_USER,
    RESTORED_USER,
    ASP_NAME,
    ASP_NUMBER
FROM RECOVERY.RJ_AUDIT_VIEW
WHERE COALESCE(PREVIOUS_USER, '') <>
      COALESCE(RESTORED_USER, '')
ORDER BY
    ENTRY_TIMESTAMP DESC;

This can reveal:

Find *RQD replaced by a named profile

SELECT
    ENTRY_TIMESTAMP,
    RESTORE_RUN_BY,
    JOB_DESCRIPTION_LIBRARY,
    JOB_DESCRIPTION,
    PREVIOUS_USER,
    RESTORED_USER
FROM RECOVERY.RJ_AUDIT_VIEW
WHERE PREVIOUS_USER = '*RQD'
  AND RESTORED_USER <> '*RQD'
ORDER BY
    ENTRY_TIMESTAMP DESC;

The final security effect still depends on QSECURITY, object authority, profile authority, submission method, and overrides.

Compare with current state

The RJ entry records what the restore introduced. QSYS2.JOB_DESCRIPTION_INFO shows the current job-description value.

SELECT
    R.ENTRY_TIMESTAMP,
    R.JOB_DESCRIPTION_LIBRARY,
    R.JOB_DESCRIPTION,
    R.PREVIOUS_USER,
    R.RESTORED_USER,
    I.AUTHORIZATION_NAME
        AS CURRENT_JOB_DESCRIPTION_USER,
    CASE
        WHEN I.JOB_DESCRIPTION IS NULL
          THEN 'JOBD NOT FOUND'
        WHEN COALESCE(I.AUTHORIZATION_NAME, '') =
             COALESCE(R.RESTORED_USER, '')
          THEN 'CURRENT MATCHES RESTORE'
        ELSE 'CHANGED AFTER RESTORE'
    END AS CURRENT_STATE
FROM RECOVERY.RJ_AUDIT_VIEW AS R
LEFT JOIN QSYS2.JOB_DESCRIPTION_INFO AS I
  ON I.JOB_DESCRIPTION_LIBRARY =
     R.JOB_DESCRIPTION_LIBRARY
 AND I.JOB_DESCRIPTION =
     R.JOB_DESCRIPTION
ORDER BY
    R.ENTRY_TIMESTAMP DESC;

A mismatch may be legitimate, but it should be correlated with later JD audit entries and approved change activity.

Correlate RJ and JD activity

A useful timeline is:

RJ   Job description restored with a named user
JD   USER parameter created or changed later

This helps answer:

Was the restored profile accepted?
Was it corrected after restore?
Who corrected it?
Was it later changed back?

See:

Audit IBM i Job Description USER Changes with SQL

Create a protected evidence table

CREATE TABLE RECOVERY.RJ_AUDIT_EVIDENCE
(
    SOURCE_SYSTEM            VARCHAR(8) NOT NULL,
    RECEIVER_LIBRARY         VARCHAR(10) NOT NULL,
    RECEIVER_NAME            VARCHAR(10) NOT NULL,
    SEQUENCE_NUMBER          DECIMAL(21, 0) NOT NULL,
    ENTRY_TIMESTAMP          TIMESTAMP NOT NULL,
    RESTORE_RUN_BY           VARCHAR(10),
    QUALIFIED_JOB_NAME       VARCHAR(28),
    PROGRAM_LIBRARY          VARCHAR(10),
    PROGRAM_NAME             VARCHAR(10),
    JOB_DESCRIPTION_LIBRARY  VARCHAR(10),
    JOB_DESCRIPTION          VARCHAR(10),
    PREVIOUS_USER            VARCHAR(10),
    RESTORED_USER            VARCHAR(10),
    ASP_NAME                 VARCHAR(10),
    ASP_NUMBER               VARCHAR(5),
    REVIEW_STATUS            VARCHAR(24) NOT NULL,
    CHANGE_REFERENCE         VARCHAR(128),
    REVIEW_NOTES             VARCHAR(2048),
    PRIMARY KEY
    (
        SOURCE_SYSTEM,
        RECEIVER_LIBRARY,
        RECEIVER_NAME,
        SEQUENCE_NUMBER
    )
);

Useful statuses include:

NEW
EXPECTED
REVIEW REQUIRED
ENVIRONMENT MISMATCH
CORRECTED
APPROVED EXCEPTION
UNAUTHORIZED

Use bounded collection

A scheduled collector should preserve:

Journal
Receiver library
Receiver name
Sequence number
Entry timestamp
Collector
Status

Do not store only sequence number because journal sequences can be reset.

A safe cycle is:

Read a bounded receiver and sequence range
Decode RJ entries
Insert into the protected evidence table
Suppress duplicates
Commit the batch
Advance the checkpoint

Use CPYAUDJRNE when formatted output is preferable

CPYAUDJRNE ENTTYP(RJ)
            JRNRCV(*CURCHAIN)
            OUTFILE(AUDITLIB/RECOVERY)

The command creates an entry-type-specific output file, typically with an RJ suffix. Query it with SQL after confirming the generated column names with QSYS2.SYSCOLUMNS, ACS schema details, or DSPFFD.

CPYAUDJRNE supports every audit entry type and copies all formatted fields, but it requires *AUDIT special authority.

Why a restored JOBD can be risky

A restored job description may be valid on the source system but unsafe on the target:

The profile has more authority on the target
The profile should not exist in nonproduction
The profile references production integration credentials
The target expected USER(*RQD)
The restore overwrote a local hardening change
The job description is publicly usable

The RJ entry shows that the configuration was restored. It does not prove that the profile was later used.

Determine where the job description is used

Investigate:

A restored value becomes an active exposure when a job-initiation path uses it.

Auditing and authority requirements

RJ evidence depends on save-and-restore auditing being active and the required QAUDJRN receivers being retained.

Review:

QAUDCTL
QAUDLVL
QAUDLVL2
*SAVRST coverage
QAUDJRN receiver retention

For QSYS2.DISPLAY_JOURNAL, the caller needs appropriate authority to the journal, its library, and every requested receiver and receiver library. Use a dedicated audit-reporting profile rather than broad special authority.

Recovery sign-off checklist

Collect RJ entries for the recovery window
Decode and validate each restored JOBD profile
Compare source and target environment policy
Review every profile difference
Correlate later JD entries
Validate current JOBD state
Review JOBD and profile authority
Identify scheduler and submission paths
Test critical jobs
Preserve receiver and sequence evidence
Obtain owner approval

Final takeaway

A restored job description can carry a user identity from one environment into another.

The RJ audit entry preserves:

The restored job description
Its library
The current named profile
The previous named profile
The ASP
The restore job
The effective user
The receiver and sequence

IBM does not currently provide a dedicated formatted AUDIT_JOURNAL_RJ SQL service.

Use QSYS2.DISPLAY_JOURNAL to retrieve the entry, decode the documented RJ format, preserve the evidence, and compare it with the target environment’s approved security model.

A successful restore confirms that an object returned. It does not confirm that the restored identity is safe.

Comments

Share your thoughts, questions, or real-world IBM i experiences related to this article.