20 min read

Mainframe systems running z/OS have long been the backbone of the global financial industry, processing trillions of dollars in transactions every day. With that responsibility comes an absolute requirement for security. Unlike distributed systems...

Chapter 31: COBOL and the z/OS Security Model

Part VI - Mainframe Environment and Batch Processing

Mainframe systems running z/OS have long been the backbone of the global financial industry, processing trillions of dollars in transactions every day. With that responsibility comes an absolute requirement for security. Unlike distributed systems where security was often an afterthought bolted on after the fact, z/OS was designed from the ground up with a comprehensive, multi-layered security architecture. As a COBOL developer working in a mainframe environment, understanding this security model is not optional --- it is fundamental to writing programs that operate correctly within the constraints of the security infrastructure and that protect the sensitive data entrusted to them.

This chapter examines the z/OS security model from the perspective of a COBOL programmer. We will explore the major external security managers, understand how security applies to datasets, programs, transactions, and databases, and learn how to write COBOL code that handles security requirements gracefully. We will also address secure coding practices, audit trail implementation, and compliance requirements that affect virtually every mainframe COBOL shop in the financial sector.

Key Concept

Security on z/OS is not something you add to an application --- it is an integral part of the operating environment. Every resource access, every transaction, and every dataset operation is subject to security checks. COBOL programs must be written with this reality in mind from the very first line of code.


31.1 z/OS Security Overview

The z/OS operating system provides security through a combination of hardware features, operating system services, and external security managers. Understanding this layered approach is essential before diving into COBOL-specific security considerations.

31.1.1 The Security Architecture

z/OS security operates on three fundamental principles:

  1. Identification --- Every user and process must have a verifiable identity.
  2. Authentication --- That identity must be proven, typically through a password, passphrase, certificate, or multi-factor authentication token.
  3. Authorization --- Once identified and authenticated, access to every resource is checked against security rules.

The System Authorization Facility (SAF) is the z/OS component that intercepts resource access requests and routes them to an External Security Manager (ESM). The SAF acts as a common interface, meaning that subsystems like CICS, DB2, and JES do not need to know which ESM is installed --- they simply issue SAF calls, and the ESM handles the rest.

31.1.2 External Security Managers

Three major external security managers dominate the mainframe market:

RACF (Resource Access Control Facility) is IBM's own ESM and is by far the most widely deployed. It is included with z/OS at no additional charge and is the de facto standard in most shops. RACF stores security information in a VSAM dataset called the RACF database and provides a comprehensive set of commands and utilities for managing security.

CA ACF2 (Access Control Facility 2) is a product originally from Computer Associates (now Broadcom). ACF2 uses a rule-based approach to security that some administrators find more intuitive for certain access patterns. It stores its data in a VSAM KSDS and uses a different command syntax than RACF, though the underlying concepts are similar.

CA Top Secret is also from Broadcom and takes a somewhat different approach by organizing security around a hierarchical structure of departments and divisions. Top Secret is known for its administrative flexibility and is popular in shops that need decentralized security administration.

Key Concept

While the specific commands differ between RACF, ACF2, and Top Secret, the fundamental concepts of users, groups, resources, and access levels apply to all three. This chapter focuses primarily on RACF as the most prevalent ESM, but the COBOL programming techniques apply regardless of which ESM is in use.

31.1.3 How Security Affects COBOL Programs

A COBOL batch program runs under the identity of the user who submitted the JCL job. A COBOL CICS program runs under the identity of the terminal user who initiated the transaction. In both cases, every resource the program attempts to access --- datasets, VSAM files, DB2 tables, CICS resources --- is subject to security checks performed by the ESM.

When a security check fails, the consequences depend on the context:

  • In batch, a dataset open failure due to security results in an ABEND, typically S913 (RACF) or a similar code.
  • In CICS, a security violation returns a RESP code that the program can check and handle.
  • In DB2, a privilege failure returns a negative SQLCODE.

As we discussed in Chapter 22 when covering file handling and in Chapter 25 when addressing error handling, COBOL programs must be prepared to handle these failures gracefully.


31.2 RACF Fundamentals

RACF is the security foundation for the vast majority of z/OS installations worldwide. Understanding its core concepts is essential for any COBOL developer.

31.2.1 Users and User IDs

Every person or process that accesses the z/OS system must have a RACF user ID. A user ID is a 1-to-8 character identifier that uniquely identifies the user within the RACF database. User profiles contain attributes such as:

  • Name --- The user's real name
  • Default group --- The primary group association
  • Owner --- Who administers this user profile
  • Password/passphrase --- Authentication credentials
  • Attributes --- Special privileges (SPECIAL, OPERATIONS, AUDITOR, etc.)

The RACF SPECIAL attribute grants a user the ability to issue any RACF command. The OPERATIONS attribute grants access to any dataset. The AUDITOR attribute allows a user to list and review security settings. These attributes are strictly controlled and should never be assigned to application user IDs.

31.2.2 Groups

Groups in RACF serve two purposes: they organize users into logical collections, and they own resources. A group can represent a department, a project team, or an application. Key group concepts include:

  • Every user has a default group assigned at logon time.
  • Users can be connected to multiple groups.
  • Groups have owners who can manage group membership.
  • Groups form a hierarchy with a top-level group (often SYS1).

In a typical banking environment, you might see groups structured as follows:

SYS1
  +-- BANKGRP        (Bank-wide group)
       +-- RETAILBK   (Retail banking division)
       |    +-- DEPOSITS  (Deposits application team)
       |    +-- LENDING   (Lending application team)
       +-- CORPBK     (Corporate banking division)
       +-- TREASURY    (Treasury operations)

31.2.3 Resource Profiles and Access Levels

RACF protects resources through profiles. A profile defines who can access a resource and at what level. Resources are organized into classes, with the most common being:

  • DATASET --- Protects datasets (files)
  • FACILITY --- Protects system facilities and functions
  • PROGRAM --- Protects load modules
  • TRANSACT --- Protects CICS transactions
  • DSNR --- Protects DB2 resources

Each profile has an access list that specifies which users and groups can access the resource and at what level. The five standard RACF access levels, from least to most permissive, are:

Access Level Description Typical Use
NONE No access permitted Explicitly deny access to a user or group
READ Read-only access Inquiry programs, report generators
UPDATE Read and write access Transaction processing programs
CONTROL VSAM control interval access VSAM utility programs, reorganization jobs
ALTER Full control including delete Dataset administrators, migration utilities

Key Concept

The principle of least privilege dictates that every user and program should have only the minimum access level required to perform its function. A COBOL program that reads a customer master file needs only READ access. A program that updates account balances needs UPDATE access. Never grant more access than necessary.

31.2.4 RACF Commands for the COBOL Developer

While COBOL developers typically do not administer RACF directly, understanding the basic commands helps when working with security administrators to resolve access issues:

//RACFCMDS JOB (ACCT),'SECURITY ADMIN',CLASS=A
//STEP1    EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  /* List a user's profile */
  LISTUSER USRT001

  /* List a dataset profile */
  LISTDSD DATASET('BANK.PROD.CUSTMAST') ALL

  /* Display access list for a dataset profile */
  LISTDSD DATASET('BANK.PROD.ACCTRANS') AUTHLIST

  /* Search for profiles matching a pattern */
  SEARCH FILTER(BANK.PROD.**)

  /* Check if a user has access to a resource */
  LISTDSD DATASET('BANK.PROD.GLMASTER') AUTHLIST
/*

31.3 Dataset Security

Datasets are the most commonly secured resources on z/OS, and COBOL programs interact with datasets constantly. Understanding how dataset security works is critical for troubleshooting access problems and designing secure applications.

31.3.1 Discrete vs. Generic Profiles

RACF supports two types of dataset profiles:

Discrete profiles protect a single, specific dataset. The profile name exactly matches the dataset name. For example, a discrete profile for BANK.PROD.CUSTMAST protects only that one dataset.

Generic profiles use pattern-matching characters to protect multiple datasets with a single profile. The two pattern characters are:

  • * --- Matches any single qualifier (one node in the dataset name)
  • ** --- Matches zero or more qualifiers

For example: - BANK.PROD.* protects all datasets with exactly three qualifiers starting with BANK.PROD - BANK.PROD.** protects all datasets starting with BANK.PROD, regardless of how many additional qualifiers follow - BANK.*.CUSTMAST protects BANK.PROD.CUSTMAST, BANK.TEST.CUSTMAST, etc.

In production banking environments, generic profiles are the norm because they allow administrators to protect entire groups of related datasets efficiently:

//* RACF commands to protect banking datasets
//* These would be run by a security administrator
//RACFPROT JOB (ACCT),'SEC ADMIN',CLASS=A
//STEP1    EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  /* Protect all production customer datasets */
  ADDSD 'BANK.PROD.CUST.**' UACC(NONE) OWNER(RETAILBK)

  /* Grant read access to the inquiry group */
  PERMIT 'BANK.PROD.CUST.**' ID(CUSTINQ) ACCESS(READ)

  /* Grant update access to the transaction processing group */
  PERMIT 'BANK.PROD.CUST.**' ID(CUSTTRAN) ACCESS(UPDATE)

  /* Grant alter access to the DBA group for reorganizations */
  PERMIT 'BANK.PROD.CUST.**' ID(DBATEAM) ACCESS(ALTER)
/*

31.3.2 Conditional Access

RACF supports conditional access, which allows access to a resource only when certain conditions are met. The most common condition is program access, which grants access to a dataset only when the request comes from a specific program. This is a powerful security mechanism for COBOL applications:

//* Grant access to the account master file ONLY when
//* the request comes from the authorized update program
//RACFCOND JOB (ACCT),'SEC ADMIN',CLASS=A
//STEP1    EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  /* Allow update access only through the authorized program */
  PERMIT 'BANK.PROD.ACCTMAST' ID(TELLERS) ACCESS(UPDATE) +
    WHEN(PROGRAM(ACCTUPDT))

  /* Allow read access through the inquiry program */
  PERMIT 'BANK.PROD.ACCTMAST' ID(TELLERS) ACCESS(READ) +
    WHEN(PROGRAM(ACCTINQY))

  /* Without a WHEN clause, tellers have no access at all */
/*

This means that even if a teller's user ID is compromised, the attacker cannot access the account master file unless they run the specific authorized programs. This is a fundamental security control in banking applications.

31.3.3 Handling Dataset Security Failures in COBOL

When a COBOL batch program attempts to open a dataset for which the user does not have the required access level, the system issues an ABEND. The most common security-related ABEND codes are:

ABEND Code Meaning Common Cause
S913 RACF access denied User lacks required access level
S913-38 RACF - dataset not cataloged and no profile Dataset does not exist or is not protected
S913-3C RACF - insufficient access User has some access but not enough (e.g., READ when UPDATE needed)
SE37 Dataset space exhaustion Not security per se, but often confused with security issues

In a COBOL batch program, you can check the file status after an OPEN to detect security failures before they escalate to ABENDs:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. ACCT-INQ.
      *================================================================*
      * ACCOUNT INQUIRY PROGRAM                                         *
      * This program reads the account master file and generates        *
      * an inquiry report. It demonstrates proper security error        *
      * handling for dataset access.                                    *
      *================================================================*

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT ACCOUNT-FILE
               ASSIGN TO ACCTMAST
               ORGANIZATION IS INDEXED
               ACCESS MODE IS RANDOM
               RECORD KEY IS ACCT-KEY
               FILE STATUS IS WS-ACCT-STATUS.

           SELECT REPORT-FILE
               ASSIGN TO RPTOUT
               FILE STATUS IS WS-RPT-STATUS.

       DATA DIVISION.
       FILE SECTION.
       FD  ACCOUNT-FILE.
       01  ACCOUNT-RECORD.
           05  ACCT-KEY.
               10  ACCT-BRANCH        PIC X(4).
               10  ACCT-NUMBER        PIC X(10).
           05  ACCT-NAME              PIC X(30).
           05  ACCT-TYPE              PIC X(2).
           05  ACCT-BALANCE           PIC S9(13)V99 COMP-3.
           05  ACCT-STATUS-CODE       PIC X(1).
           05  ACCT-LAST-ACTIVITY     PIC X(8).
           05  FILLER                 PIC X(42).

       FD  REPORT-FILE.
       01  REPORT-RECORD              PIC X(133).

       WORKING-STORAGE SECTION.
       01  WS-FILE-STATUSES.
           05  WS-ACCT-STATUS         PIC XX.
               88  ACCT-SUCCESS       VALUE '00'.
               88  ACCT-NOT-FOUND     VALUE '23'.
               88  ACCT-SECURITY-ERR  VALUE '90' '93'.
           05  WS-RPT-STATUS          PIC XX.

       01  WS-ERROR-MESSAGE           PIC X(80).
       01  WS-RETURN-CODE             PIC S9(4) COMP VALUE 0.

       01  WS-AUDIT-RECORD.
           05  WS-AUDIT-TIMESTAMP     PIC X(26).
           05  WS-AUDIT-USERID        PIC X(8).
           05  WS-AUDIT-ACTION        PIC X(10).
           05  WS-AUDIT-RESOURCE      PIC X(44).
           05  WS-AUDIT-RESULT        PIC X(8).
           05  WS-AUDIT-DETAIL        PIC X(80).

       PROCEDURE DIVISION.
       0000-MAIN-LOGIC.
           PERFORM 1000-INITIALIZE
           IF WS-RETURN-CODE = 0
               PERFORM 2000-PROCESS-INQUIRY
           END-IF
           PERFORM 9000-TERMINATE
           STOP RUN.

       1000-INITIALIZE.
           OPEN INPUT ACCOUNT-FILE
           EVALUATE TRUE
               WHEN ACCT-SUCCESS
                   CONTINUE
               WHEN ACCT-SECURITY-ERR
                   STRING 'SECURITY ERROR OPENING ACCOUNT FILE. '
                          'FILE STATUS: ' WS-ACCT-STATUS
                          '. CONTACT SECURITY ADMINISTRATOR.'
                       DELIMITED BY SIZE
                       INTO WS-ERROR-MESSAGE
                   DISPLAY WS-ERROR-MESSAGE UPON CONSOLE
                   MOVE 16 TO WS-RETURN-CODE
                   MOVE 'SECURITY' TO WS-AUDIT-RESULT
                   PERFORM 8000-WRITE-AUDIT-RECORD
               WHEN OTHER
                   STRING 'ERROR OPENING ACCOUNT FILE. '
                          'FILE STATUS: ' WS-ACCT-STATUS
                       DELIMITED BY SIZE
                       INTO WS-ERROR-MESSAGE
                   DISPLAY WS-ERROR-MESSAGE UPON CONSOLE
                   MOVE 12 TO WS-RETURN-CODE
           END-EVALUATE.

           IF WS-RETURN-CODE = 0
               OPEN OUTPUT REPORT-FILE
               IF NOT WS-RPT-STATUS = '00'
                   STRING 'ERROR OPENING REPORT FILE. '
                          'FILE STATUS: ' WS-RPT-STATUS
                       DELIMITED BY SIZE
                       INTO WS-ERROR-MESSAGE
                   DISPLAY WS-ERROR-MESSAGE UPON CONSOLE
                   MOVE 12 TO WS-RETURN-CODE
               END-IF
           END-IF.

       2000-PROCESS-INQUIRY.
           CONTINUE.
      *    (Inquiry processing logic would go here)

       8000-WRITE-AUDIT-RECORD.
           MOVE FUNCTION CURRENT-DATE TO WS-AUDIT-TIMESTAMP
           MOVE 'ACCTMAST'            TO WS-AUDIT-RESOURCE
           MOVE 'OPEN'                TO WS-AUDIT-ACTION
           DISPLAY 'AUDIT: ' WS-AUDIT-RECORD UPON CONSOLE.

       9000-TERMINATE.
           IF ACCT-SUCCESS
               CLOSE ACCOUNT-FILE
           END-IF
           CLOSE REPORT-FILE
           MOVE WS-RETURN-CODE TO RETURN-CODE.

The JCL to execute this program with appropriate security context:

//ACCTINQ  JOB (BANK,RETAIL),'ACCOUNT INQUIRY',
//             CLASS=A,MSGCLASS=X,MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID
//*
//*================================================================*
//* ACCOUNT INQUIRY JOB                                             *
//* This job runs under the submitter's security context.           *
//* The user must have READ access to BANK.PROD.ACCTMAST            *
//* via RACF profile BANK.PROD.CUST.**                              *
//*================================================================*
//*
//INQUIRY  EXEC PGM=ACCTINQ
//STEPLIB  DD DSN=BANK.PROD.LOADLIB,DISP=SHR
//ACCTMAST DD DSN=BANK.PROD.ACCTMAST,DISP=SHR
//RPTOUT   DD SYSOUT=*
//SYSOUT   DD SYSOUT=*

31.4 Program Security

Beyond protecting data, z/OS also provides mechanisms to control which programs can be executed and what authority those programs carry.

31.4.1 Program-Controlled Access

RACF's program access facility allows administrators to restrict which programs can access certain datasets. This is the mechanism behind conditional access as described in the previous section. For program control to work:

  1. The program must be defined in the RACF PROGRAM class.
  2. The program library must be APF-authorized or the program must be in a controlled library.
  3. The dataset profile must have conditional access rules specifying the program.
//* Define programs in the RACF PROGRAM class
//PROGDEF  JOB (ACCT),'SEC ADMIN',CLASS=A
//STEP1    EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  /* Define the account update program */
  RDEFINE PROGRAM ACCTUPDT ADDMEM('BANK.PROD.LOADLIB'//NOPADCHK)

  /* Define the account inquiry program */
  RDEFINE PROGRAM ACCTINQY ADDMEM('BANK.PROD.LOADLIB'//NOPADCHK)

  /* Activate program control */
  SETROPTS WHEN(PROGRAM) REFRESH
/*

31.4.2 APF Authorization

The Authorized Program Facility (APF) allows designated programs to execute privileged instructions and access restricted system services. APF-authorized programs run in supervisor state or with a system key, giving them extraordinary power over the system.

As a COBOL programmer, you generally do not write APF-authorized programs. However, you need to understand APF authorization because:

  • Your program's STEPLIB or JOBLIB must not inadvertently include APF-authorized libraries mixed with non-authorized libraries, as this can cause S306 ABENDs.
  • Some system services your program calls may require APF authorization.
  • Security violations related to APF can produce ABENDs such as S306 and S806.
//*================================================================*
//* IMPORTANT: Do not mix APF-authorized and non-authorized         *
//* libraries in the same STEPLIB concatenation.                    *
//*================================================================*
//*
//* INCORRECT - mixing authorized and non-authorized:
//* //STEPLIB  DD DSN=SYS1.AUTHLIB,DISP=SHR        (APF-authorized)
//* //         DD DSN=BANK.PROD.LOADLIB,DISP=SHR    (not authorized)
//*
//* CORRECT - use only the application load library:
//STEPLIB  DD DSN=BANK.PROD.LOADLIB,DISP=SHR

31.4.3 Program Signing and Integrity

Modern z/OS environments can use program signing to ensure that load modules have not been tampered with. While the signing process itself is handled by system programmers and build tools, COBOL developers should be aware that:

  • Programs compiled and link-edited in controlled build environments can be digitally signed.
  • z/OS can verify signatures at program load time, rejecting modified or unsigned programs.
  • This prevents unauthorized modifications to production COBOL programs, which is a critical control for financial applications.

31.5 CICS Security

CICS (Customer Information Control System) is the most widely used online transaction processing system on z/OS, and it has its own comprehensive security framework that works in conjunction with the external security manager. As discussed in Chapter 23, CICS programs operate in a unique environment, and security adds another important dimension.

31.5.1 Transaction Security

Every CICS transaction has a four-character transaction ID. RACF can control which users are authorized to execute which transactions through the TRANSACT resource class (or the GCICSTRN general resource class):

//* Define CICS transaction security
//CICSSEC  JOB (ACCT),'SEC ADMIN',CLASS=A
//STEP1    EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  /* Define transaction security for account transfer */
  RDEFINE TCICSTRN XFRA UACC(NONE)

  /* Allow senior tellers to execute account transfers */
  PERMIT XFRA CLASS(TCICSTRN) ID(SRTELLER) ACCESS(READ)

  /* Define transaction security for balance inquiry */
  RDEFINE TCICSTRN BINQ UACC(NONE)

  /* Allow all tellers to execute balance inquiry */
  PERMIT BINQ CLASS(TCICSTRN) ID(TELLERS) ACCESS(READ)

  /* Refresh the in-storage profiles */
  SETROPTS RACLIST(TCICSTRN) REFRESH
/*

31.5.2 Resource Security

Beyond transactions, CICS protects individual resources such as files, programs, transient data queues, and temporary storage queues. A COBOL program that accesses a CICS file will be subject to both the CICS resource security check and the underlying dataset security check.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. XFRA-PGM.
      *================================================================*
      * ACCOUNT TRANSFER PROGRAM - CICS                                 *
      * Demonstrates security handling in a CICS environment.           *
      * Cross-reference: Chapter 23 (CICS Programming),                 *
      *                  Chapter 25 (Error Handling)                     *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-RESP                    PIC S9(8) COMP.
       01  WS-RESP2                   PIC S9(8) COMP.
       01  WS-TRANID                  PIC X(4).
       01  WS-USERID                  PIC X(8).
       01  WS-ERROR-MSG               PIC X(79).

       01  WS-TRANSFER-DATA.
           05  WS-FROM-ACCOUNT        PIC X(10).
           05  WS-TO-ACCOUNT          PIC X(10).
           05  WS-TRANSFER-AMOUNT     PIC S9(13)V99 COMP-3.

       01  WS-ACCT-RECORD.
           05  WS-ACCT-KEY            PIC X(14).
           05  WS-ACCT-NAME           PIC X(30).
           05  WS-ACCT-BALANCE        PIC S9(13)V99 COMP-3.
           05  WS-ACCT-STATUS         PIC X(1).
               88  ACCT-ACTIVE        VALUE 'A'.
               88  ACCT-FROZEN        VALUE 'F'.
               88  ACCT-CLOSED        VALUE 'C'.
           05  FILLER                 PIC X(42).

       01  WS-SECURITY-DATA.
           05  WS-SEC-AUTHORITY       PIC S9(8) COMP.
           05  WS-SEC-TRANSFER-LIMIT  PIC S9(13)V99 COMP-3.

       COPY DFHBMSCA.

       PROCEDURE DIVISION.
       0000-MAIN-LOGIC.
      *    Retrieve the user ID for audit and authorization
           EXEC CICS ASSIGN
               USERID(WS-USERID)
               RESP(WS-RESP)
           END-EXEC

           IF WS-RESP NOT = DFHRESP(NORMAL)
               MOVE 'UNABLE TO DETERMINE USER IDENTITY'
                   TO WS-ERROR-MSG
               PERFORM 9000-SEND-ERROR
               EXEC CICS RETURN END-EXEC
           END-IF

      *    Verify the user is authorized for the transfer amount
           PERFORM 1000-CHECK-AUTHORIZATION

      *    If authorized, perform the transfer
           IF WS-RESP = DFHRESP(NORMAL)
               PERFORM 2000-PROCESS-TRANSFER
           END-IF

           EXEC CICS RETURN END-EXEC.

       1000-CHECK-AUTHORIZATION.
      *    Query a custom authorization resource to check
      *    the user's transfer authority level
           EXEC CICS QUERY SECURITY
               RESTYPE('FACILIT')
               RESID('BANK.XFER.HIGH')
               RESIDLENGTH(14)
               LOGMESSAGE(WS-ERROR-MSG)
               RESP(WS-RESP)
               RESP2(WS-RESP2)
           END-EXEC

           EVALUATE WS-RESP
               WHEN DFHRESP(NORMAL)
      *            User has high-value transfer authority
                   MOVE 999999999.99 TO WS-SEC-TRANSFER-LIMIT

               WHEN DFHRESP(NOTAUTH)
      *            Check for standard transfer authority
                   EXEC CICS QUERY SECURITY
                       RESTYPE('FACILIT')
                       RESID('BANK.XFER.STD')
                       RESIDLENGTH(13)
                       RESP(WS-RESP)
                       RESP2(WS-RESP2)
                   END-EXEC

                   IF WS-RESP = DFHRESP(NORMAL)
                       MOVE 50000.00 TO WS-SEC-TRANSFER-LIMIT
                   ELSE
                       MOVE 'YOU ARE NOT AUTHORIZED FOR TRANSFERS'
                           TO WS-ERROR-MSG
                       PERFORM 9000-SEND-ERROR
                       PERFORM 8500-LOG-SECURITY-VIOLATION
                   END-IF

               WHEN OTHER
                   MOVE 'SECURITY SYSTEM ERROR - CONTACT SUPPORT'
                       TO WS-ERROR-MSG
                   PERFORM 9000-SEND-ERROR
           END-EVALUATE.

      *    Check if the transfer amount exceeds the user's limit
           IF WS-TRANSFER-AMOUNT > WS-SEC-TRANSFER-LIMIT
               STRING 'TRANSFER AMOUNT EXCEEDS YOUR LIMIT OF '
                      WS-SEC-TRANSFER-LIMIT
                   DELIMITED BY SIZE INTO WS-ERROR-MSG
               PERFORM 9000-SEND-ERROR
               PERFORM 8500-LOG-SECURITY-VIOLATION
               MOVE DFHRESP(NOTAUTH) TO WS-RESP
           END-IF.

       2000-PROCESS-TRANSFER.
      *    Read the source account with update intent
           MOVE WS-FROM-ACCOUNT TO WS-ACCT-KEY
           EXEC CICS READ
               FILE('ACCTMAST')
               INTO(WS-ACCT-RECORD)
               RIDFLD(WS-ACCT-KEY)
               UPDATE
               RESP(WS-RESP)
               RESP2(WS-RESP2)
           END-EXEC

           EVALUATE WS-RESP
               WHEN DFHRESP(NORMAL)
                   CONTINUE
               WHEN DFHRESP(NOTFND)
                   MOVE 'SOURCE ACCOUNT NOT FOUND'
                       TO WS-ERROR-MSG
                   PERFORM 9000-SEND-ERROR
               WHEN DFHRESP(NOTAUTH)
      *            CICS file security denied access
                   MOVE 'NOT AUTHORIZED TO ACCESS ACCOUNT FILE'
                       TO WS-ERROR-MSG
                   PERFORM 9000-SEND-ERROR
                   PERFORM 8500-LOG-SECURITY-VIOLATION
               WHEN DFHRESP(DISABLED)
                   MOVE 'ACCOUNT FILE CURRENTLY DISABLED'
                       TO WS-ERROR-MSG
                   PERFORM 9000-SEND-ERROR
               WHEN OTHER
                   MOVE 'UNEXPECTED ERROR READING ACCOUNT FILE'
                       TO WS-ERROR-MSG
                   PERFORM 9000-SEND-ERROR
           END-EVALUATE.

       8500-LOG-SECURITY-VIOLATION.
      *    Write a security violation record to the audit log
      *    This uses a transient data queue monitored by security
           EXEC CICS WRITEQ TD
               QUEUE('SECL')
               FROM(WS-ERROR-MSG)
               LENGTH(LENGTH OF WS-ERROR-MSG)
               RESP(WS-RESP)
           END-EXEC.

       9000-SEND-ERROR.
           EXEC CICS SEND TEXT
               FROM(WS-ERROR-MSG)
               LENGTH(LENGTH OF WS-ERROR-MSG)
               FREEKB
               ERASE
               RESP(WS-RESP)
           END-EXEC.

31.5.3 DFHCOMMAREA Security Considerations

The DFHCOMMAREA (Communication Area) is the primary mechanism for passing data between CICS programs and between transaction executions in a pseudo-conversational program. As discussed in Chapter 23, security of the COMMAREA is an important concern:

  • Data in the COMMAREA is not encrypted by default. Sensitive data such as account numbers, PINs, or Social Security numbers should not be stored in plain text in the COMMAREA.
  • COMMAREA can be viewed using CICS diagnostic tools (CECI, CEDF), so sensitive data is exposed to anyone with access to these tools.
  • COMMAREA tampering is possible if a user uses CECI to start a transaction with a fabricated COMMAREA.

Key Concept

Never trust data in the COMMAREA without validation. Always verify that the user ID in the current transaction matches any user ID stored in the COMMAREA from a previous interaction. Re-validate authorization on every transaction invocation, because a pseudo-conversational program may resume minutes or hours later when security context could have changed.

       01  WS-COMMAREA.
           05  CA-TRANS-STATE         PIC X(2).
               88  CA-INITIAL         VALUE 'IN'.
               88  CA-CONFIRMED       VALUE 'CF'.
           05  CA-USERID-SAVED        PIC X(8).
           05  CA-FROM-ACCOUNT        PIC X(10).
           05  CA-TO-ACCOUNT          PIC X(10).
           05  CA-AMOUNT              PIC S9(13)V99 COMP-3.
           05  CA-TIMESTAMP           PIC X(26).
           05  CA-INTEGRITY-HASH      PIC X(32).

       PROCEDURE DIVISION.
       0000-MAIN-LOGIC.
      *    Retrieve current user identity
           EXEC CICS ASSIGN
               USERID(WS-CURRENT-USERID)
               RESP(WS-RESP)
           END-EXEC

      *    If returning from a previous interaction, validate
           IF EIBCALEN > 0
               MOVE DFHCOMMAREA TO WS-COMMAREA
      *        CRITICAL: Verify the user hasn't changed
               IF CA-USERID-SAVED NOT = WS-CURRENT-USERID
                   MOVE 'SESSION INTEGRITY VIOLATION - '
                       & 'USER IDENTITY MISMATCH'
                       TO WS-ERROR-MSG
                   PERFORM 8500-LOG-SECURITY-VIOLATION
                   PERFORM 9000-SEND-ERROR
                   EXEC CICS RETURN END-EXEC
               END-IF
      *        Validate the integrity hash to detect tampering
               PERFORM 1500-VERIFY-INTEGRITY-HASH
           ELSE
      *        First invocation - initialize
               INITIALIZE WS-COMMAREA
               MOVE 'IN' TO CA-TRANS-STATE
               MOVE WS-CURRENT-USERID TO CA-USERID-SAVED
           END-IF.

31.6 DB2 Security

DB2 for z/OS has its own authorization mechanism that works alongside the external security manager. COBOL programs that access DB2 data --- as covered in Chapter 24 --- must operate within both the RACF security framework and the DB2 authorization framework.

31.6.1 DB2 Privileges and GRANT/REVOKE

DB2 uses a privilege-based authorization model. Privileges can be granted at multiple levels:

System-level privileges control administrative functions: - SYSADM --- Full DB2 system authority - SYSCTRL --- System control without data access - DBADM --- Database administration

Object-level privileges control access to specific database objects:

-- Grant SELECT access on the customer table to the inquiry group
GRANT SELECT ON TABLE BANKDB.CUSTOMER
    TO CUSTINQ;

-- Grant INSERT, UPDATE on the transaction table
-- to the transaction processing auth ID
GRANT INSERT, UPDATE ON TABLE BANKDB.ACCT_TRANS
    TO CUSTTRAN;

-- Grant EXECUTE on a stored procedure
GRANT EXECUTE ON PROCEDURE BANKDB.CALC_INTEREST
    TO BATCHJOB;

-- Revoke access when no longer needed
REVOKE UPDATE ON TABLE BANKDB.CUSTOMER
    FROM TEMPUSER;

31.6.2 Plans and Packages

When a COBOL program containing embedded SQL is compiled and bound, DB2 creates a plan or package that contains the access paths for the SQL statements. Access to these plans and packages is itself controlled by DB2 authorization:

//*================================================================*
//* BIND PLAN FOR THE ACCOUNT TRANSFER PROGRAM                      *
//* The auth ID performing the bind must have BINDADD authority      *
//*================================================================*
//BINDPLAN EXEC PGM=IKJEFT01,DYNAMNBR=20
//STEPLIB  DD DSN=DSN.V13R1.SDSNLOAD,DISP=SHR
//DBRMLIB  DD DSN=BANK.PROD.DBRMLIB(XFRAPGM),DISP=SHR
//SYSTSPRT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//SYSTSIN  DD *
  DSN SYSTEM(DB2P)
  BIND PLAN(XFRAPLAN) -
       MEMBER(XFRAPGM) -
       ACTION(REPLACE) -
       RETAIN -
       ISOLATION(CS) -
       VALIDATE(BIND) -
       OWNER(CUSTTRAN) -
       QUALIFIER(BANKDB)
  GRANT EXECUTE ON PLAN(XFRAPLAN) TO TELLERS
  END
/*

The OWNER parameter is critical --- it determines which authorization ID is used when the SQL in the plan is executed. By setting the OWNER to an authorization ID with the required table privileges, individual users do not need direct table access. They only need EXECUTE authority on the plan.

Key Concept

DB2 plan-level security provides an important layer of defense in depth. Users need EXECUTE authority on the plan, and the plan's OWNER must have privileges on the underlying tables. This means you can control exactly which SQL operations users can perform, mediated through bound plans. This is conceptually similar to RACF's program-controlled access for datasets.

31.6.3 Handling DB2 Security Errors in COBOL

When a DB2 security check fails, specific SQLCODEs are returned that your COBOL program must handle:

SQLCODE Meaning
-551 User does not have the required privilege on an object
-552 User does not have the required authorization
-553 Cannot grant/revoke because of missing authority
       WORKING-STORAGE SECTION.
           EXEC SQL INCLUDE SQLCA END-EXEC.

       01  WS-ERROR-HANDLING.
           05  WS-SQL-ERROR-MSG       PIC X(200).
           05  WS-DB2-SECURITY-ERR    PIC 9 VALUE 0.
               88  DB2-SEC-ERROR      VALUE 1.
           05  WS-SQLCODE-DISPLAY     PIC -(5)9.

       PROCEDURE DIVISION.
       2000-UPDATE-ACCOUNT.
           EXEC SQL
               UPDATE BANKDB.ACCT_MASTER
               SET    ACCT_BALANCE = ACCT_BALANCE
                                   - :WS-TRANSFER-AMOUNT,
                      LAST_ACTIVITY_DATE = CURRENT DATE,
                      LAST_ACTIVITY_TIME = CURRENT TIME,
                      LAST_TRANS_USERID  = :WS-USERID
               WHERE  ACCT_NUMBER = :WS-FROM-ACCOUNT
               AND    ACCT_STATUS = 'A'
           END-EXEC

           EVALUATE SQLCODE
               WHEN 0
                   CONTINUE
               WHEN -551
                   MOVE 1 TO WS-DB2-SECURITY-ERR
                   MOVE SQLCODE TO WS-SQLCODE-DISPLAY
                   STRING 'DB2 AUTHORIZATION FAILURE: '
                          'SQLCODE=' WS-SQLCODE-DISPLAY
                          ' USER=' WS-USERID
                          ' DOES NOT HAVE UPDATE PRIVILEGE'
                          ' ON BANKDB.ACCT_MASTER'
                       DELIMITED BY SIZE
                       INTO WS-SQL-ERROR-MSG
                   PERFORM 8000-LOG-DB2-ERROR
                   PERFORM 8500-LOG-SECURITY-VIOLATION
               WHEN -552
                   MOVE 1 TO WS-DB2-SECURITY-ERR
                   MOVE SQLCODE TO WS-SQLCODE-DISPLAY
                   STRING 'DB2 AUTHORIZATION FAILURE: '
                          'SQLCODE=' WS-SQLCODE-DISPLAY
                          ' USER=' WS-USERID
                          ' LACKS REQUIRED AUTHORIZATION'
                       DELIMITED BY SIZE
                       INTO WS-SQL-ERROR-MSG
                   PERFORM 8000-LOG-DB2-ERROR
                   PERFORM 8500-LOG-SECURITY-VIOLATION
               WHEN -911
                   PERFORM 2500-HANDLE-DEADLOCK
               WHEN -904
                   PERFORM 2600-HANDLE-UNAVAILABLE
               WHEN OTHER
                   MOVE SQLCODE TO WS-SQLCODE-DISPLAY
                   STRING 'DB2 ERROR: SQLCODE='
                          WS-SQLCODE-DISPLAY
                          ' UPDATING ACCOUNT '
                          WS-FROM-ACCOUNT
                       DELIMITED BY SIZE
                       INTO WS-SQL-ERROR-MSG
                   PERFORM 8000-LOG-DB2-ERROR
           END-EVALUATE.

31.7 Security in COBOL Programs: Handling Security Errors

Building on the individual examples above, this section provides a comprehensive approach to handling security errors across all subsystems in a COBOL program.

31.7.1 Centralized Security Error Handling

A well-designed COBOL application centralizes security error handling through a common error-handling module. This ensures consistent logging, notification, and response to security events:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. SECERRHD.
      *================================================================*
      * SECURITY ERROR HANDLER - COMMON MODULE                          *
      * Called by all application programs when a security error         *
      * is detected. Logs the error, sends notifications, and           *
      * returns an appropriate action code to the caller.               *
      *                                                                 *
      * Cross-reference: Chapter 26 (Subprogram Design)                 *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CURRENT-TIMESTAMP       PIC X(26).
       01  WS-FORMATTED-MSG           PIC X(500).
       01  WS-MSG-LENGTH              PIC S9(4) COMP.
       01  WS-SEVERITY-TEXT           PIC X(8).
       01  WS-WTO-MSG                 PIC X(126).

       LINKAGE SECTION.
       01  LS-SEC-ERROR-BLOCK.
           05  LS-ERROR-TYPE          PIC X(8).
               88  LS-DATASET-ERROR   VALUE 'DATASET '.
               88  LS-CICS-ERROR      VALUE 'CICS    '.
               88  LS-DB2-ERROR       VALUE 'DB2     '.
               88  LS-PROGRAM-ERROR   VALUE 'PROGRAM '.
           05  LS-ERROR-CODE          PIC X(10).
           05  LS-RESOURCE-NAME       PIC X(44).
           05  LS-USER-ID             PIC X(8).
           05  LS-PROGRAM-NAME        PIC X(8).
           05  LS-ERROR-DETAIL        PIC X(200).
           05  LS-SEVERITY            PIC 9.
               88  LS-SEV-WARNING     VALUE 1.
               88  LS-SEV-ERROR       VALUE 2.
               88  LS-SEV-CRITICAL    VALUE 3.
           05  LS-ACTION-CODE         PIC X(2).
               88  LS-ACTION-CONTINUE VALUE 'CO'.
               88  LS-ACTION-RETRY    VALUE 'RE'.
               88  LS-ACTION-ABORT    VALUE 'AB'.
               88  LS-ACTION-NOTIFY   VALUE 'NO'.

       PROCEDURE DIVISION USING LS-SEC-ERROR-BLOCK.
       0000-MAIN-LOGIC.
           MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIMESTAMP

           EVALUATE TRUE
               WHEN LS-SEV-WARNING
                   MOVE 'WARNING ' TO WS-SEVERITY-TEXT
                   MOVE 'CO' TO LS-ACTION-CODE
               WHEN LS-SEV-ERROR
                   MOVE 'ERROR   ' TO WS-SEVERITY-TEXT
                   MOVE 'AB' TO LS-ACTION-CODE
               WHEN LS-SEV-CRITICAL
                   MOVE 'CRITICAL' TO WS-SEVERITY-TEXT
                   MOVE 'AB' TO LS-ACTION-CODE
           END-EVALUATE

      *    Format the security error message
           STRING WS-CURRENT-TIMESTAMP DELIMITED SIZE
                  ' SECURITY ' DELIMITED SIZE
                  WS-SEVERITY-TEXT DELIMITED SPACES
                  ': TYPE=' DELIMITED SIZE
                  LS-ERROR-TYPE DELIMITED SPACES
                  ' CODE=' DELIMITED SIZE
                  LS-ERROR-CODE DELIMITED SPACES
                  ' RESOURCE=' DELIMITED SIZE
                  LS-RESOURCE-NAME DELIMITED SPACES
                  ' USER=' DELIMITED SIZE
                  LS-USER-ID DELIMITED SPACES
                  ' PROGRAM=' DELIMITED SIZE
                  LS-PROGRAM-NAME DELIMITED SPACES
               INTO WS-FORMATTED-MSG
           END-STRING

      *    Write to the security audit log dataset
           PERFORM 1000-WRITE-AUDIT-LOG

      *    For critical errors, issue a WTO to alert operators
           IF LS-SEV-CRITICAL
               STRING 'BANK SECURITY ALERT: '
                      LS-ERROR-TYPE
                      ' VIOLATION BY USER '
                      LS-USER-ID
                      ' ON RESOURCE '
                      LS-RESOURCE-NAME
                   DELIMITED BY SIZE INTO WS-WTO-MSG
               DISPLAY WS-WTO-MSG UPON CONSOLE
           END-IF

           GOBACK.

       1000-WRITE-AUDIT-LOG.
      *    Write the formatted security event to the audit dataset
      *    In a production environment, this would write to a
      *    secured sequential dataset or DB2 audit table
           DISPLAY WS-FORMATTED-MSG UPON CONSOLE.

31.7.2 Common Security ABEND Codes

The following table summarizes the most important security-related ABEND codes that COBOL programmers encounter on z/OS:

ABEND Code Source Description Typical Resolution
S913 RACF Access denied to dataset Request appropriate RACF access
S913-14 RACF Dataset not defined to RACF and PROTECTALL is active Define a RACF profile for the dataset
S913-38 RACF No profile found for the dataset Create a RACF profile
S913-3C RACF Insufficient access level Request higher access level
S922 z/OS Too many password attempts, user revoked Contact security administrator
S306 z/OS Module not found or not APF-authorized Check STEPLIB; do not mix authorized and non-authorized libraries
S806 z/OS Module not found in library Check STEPLIB/JOBLIB; verify module name
ASRA CICS Program exception (may be security-related) Check CICS logs; verify resource security
AEY7 CICS Security violation in CICS Check CICS security definitions

31.8 Secure Coding Practices

Writing secure COBOL programs goes beyond handling security errors. It requires adopting coding practices that prevent vulnerabilities from being introduced in the first place.

31.8.1 Input Validation

Every piece of data that enters a COBOL program from an external source must be validated. This includes data from terminals, files, databases, communication areas, and inter-program calls. Failing to validate input can lead to data corruption, security breaches, and system ABENDs.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. INPUTVAL.
      *================================================================*
      * INPUT VALIDATION MODULE                                         *
      * Demonstrates comprehensive input validation for a               *
      * banking transaction entry screen.                               *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-VALIDATION-FLAGS.
           05  WS-VALID-INPUT         PIC 9 VALUE 0.
               88  INPUT-IS-VALID     VALUE 1.
               88  INPUT-IS-INVALID   VALUE 0.
           05  WS-ERROR-COUNT         PIC 99 VALUE 0.

       01  WS-ERROR-TABLE.
           05  WS-MAX-ERRORS          PIC 99 VALUE 10.
           05  WS-ERROR-ENTRY OCCURS 10 TIMES.
               10  WS-ERR-FIELD       PIC X(20).
               10  WS-ERR-MESSAGE     PIC X(50).

       LINKAGE SECTION.
       01  LS-TRANSACTION-INPUT.
           05  LS-ACCOUNT-NUMBER      PIC X(10).
           05  LS-TRANS-TYPE          PIC X(2).
               88  LS-VALID-TRANS-TYPE
                   VALUE 'DP' 'WD' 'XF' 'PY' 'FE'.
           05  LS-AMOUNT-TEXT         PIC X(15).
           05  LS-TARGET-ACCOUNT      PIC X(10).
           05  LS-REFERENCE           PIC X(30).
           05  LS-OPERATOR-ID         PIC X(8).

       PROCEDURE DIVISION USING LS-TRANSACTION-INPUT.
       0000-VALIDATE-INPUT.
           MOVE 0 TO WS-ERROR-COUNT
           MOVE 0 TO WS-VALID-INPUT

      *    Validate account number - must be numeric
           IF LS-ACCOUNT-NUMBER IS NOT NUMERIC
               PERFORM 9100-ADD-ERROR-ENTRY
               MOVE 'ACCOUNT NUMBER' TO
                   WS-ERR-FIELD(WS-ERROR-COUNT)
               MOVE 'ACCOUNT NUMBER MUST BE ALL NUMERIC'
                   TO WS-ERR-MESSAGE(WS-ERROR-COUNT)
           END-IF

      *    Check for null/blank account number
           IF LS-ACCOUNT-NUMBER = SPACES OR LOW-VALUES
               PERFORM 9100-ADD-ERROR-ENTRY
               MOVE 'ACCOUNT NUMBER' TO
                   WS-ERR-FIELD(WS-ERROR-COUNT)
               MOVE 'ACCOUNT NUMBER IS REQUIRED'
                   TO WS-ERR-MESSAGE(WS-ERROR-COUNT)
           END-IF

      *    Validate transaction type against allowed values
           IF NOT LS-VALID-TRANS-TYPE
               PERFORM 9100-ADD-ERROR-ENTRY
               MOVE 'TRANS TYPE' TO
                   WS-ERR-FIELD(WS-ERROR-COUNT)
               MOVE 'INVALID TRANSACTION TYPE'
                   TO WS-ERR-MESSAGE(WS-ERROR-COUNT)
           END-IF

      *    Validate amount - must be numeric, positive, reasonable
           IF LS-AMOUNT-TEXT IS NOT NUMERIC
               PERFORM 9100-ADD-ERROR-ENTRY
               MOVE 'AMOUNT' TO
                   WS-ERR-FIELD(WS-ERROR-COUNT)
               MOVE 'AMOUNT MUST BE NUMERIC'
                   TO WS-ERR-MESSAGE(WS-ERROR-COUNT)
           ELSE
      *        Check for negative or zero amount
               IF FUNCTION NUMVAL(LS-AMOUNT-TEXT) <= 0
                   PERFORM 9100-ADD-ERROR-ENTRY
                   MOVE 'AMOUNT' TO
                       WS-ERR-FIELD(WS-ERROR-COUNT)
                   MOVE 'AMOUNT MUST BE GREATER THAN ZERO'
                       TO WS-ERR-MESSAGE(WS-ERROR-COUNT)
               END-IF
      *        Check for unreasonably large amount
               IF FUNCTION NUMVAL(LS-AMOUNT-TEXT) >
                   99999999.99
                   PERFORM 9100-ADD-ERROR-ENTRY
                   MOVE 'AMOUNT' TO
                       WS-ERR-FIELD(WS-ERROR-COUNT)
                   MOVE 'AMOUNT EXCEEDS MAXIMUM ALLOWED VALUE'
                       TO WS-ERR-MESSAGE(WS-ERROR-COUNT)
               END-IF
           END-IF

      *    For transfers, validate target account
           IF LS-TRANS-TYPE = 'XF'
               IF LS-TARGET-ACCOUNT = SPACES
                   PERFORM 9100-ADD-ERROR-ENTRY
                   MOVE 'TARGET ACCOUNT' TO
                       WS-ERR-FIELD(WS-ERROR-COUNT)
                   MOVE 'TARGET ACCOUNT REQUIRED FOR TRANSFERS'
                       TO WS-ERR-MESSAGE(WS-ERROR-COUNT)
               END-IF
               IF LS-TARGET-ACCOUNT = LS-ACCOUNT-NUMBER
                   PERFORM 9100-ADD-ERROR-ENTRY
                   MOVE 'TARGET ACCOUNT' TO
                       WS-ERR-FIELD(WS-ERROR-COUNT)
                   MOVE 'CANNOT TRANSFER TO SAME ACCOUNT'
                       TO WS-ERR-MESSAGE(WS-ERROR-COUNT)
               END-IF
           END-IF

      *    Check for injection attempts in the reference field
      *    (SQL special characters that could indicate an attack)
           INSPECT LS-REFERENCE
               TALLYING WS-ERROR-COUNT
               FOR ALL "'" ALL '"' ALL ";" ALL "--"

           IF WS-ERROR-COUNT > 0
      *        Log a security event - possible injection attempt
               PERFORM 8500-LOG-INJECTION-ATTEMPT
               MOVE 0 TO WS-ERROR-COUNT
               PERFORM 9100-ADD-ERROR-ENTRY
               MOVE 'REFERENCE' TO
                   WS-ERR-FIELD(WS-ERROR-COUNT)
               MOVE 'INVALID CHARACTERS IN REFERENCE FIELD'
                   TO WS-ERR-MESSAGE(WS-ERROR-COUNT)
           END-IF

      *    Set overall validation result
           IF WS-ERROR-COUNT = 0
               MOVE 1 TO WS-VALID-INPUT
           END-IF

           GOBACK.

       8500-LOG-INJECTION-ATTEMPT.
           DISPLAY 'SECURITY: POSSIBLE INJECTION ATTEMPT BY USER '
               LS-OPERATOR-ID ' IN REFERENCE FIELD'
               UPON CONSOLE.

       9100-ADD-ERROR-ENTRY.
           ADD 1 TO WS-ERROR-COUNT.
           IF WS-ERROR-COUNT > WS-MAX-ERRORS
               MOVE WS-MAX-ERRORS TO WS-ERROR-COUNT
           END-IF.

31.8.2 Buffer Overflow Prevention

While COBOL is inherently safer than C/C++ with respect to buffer overflows because of its fixed-length fields, there are still scenarios where data can overflow boundaries:

      *================================================================*
      * SAFE STRING HANDLING EXAMPLES                                   *
      *================================================================*

       01  WS-INPUT-BUFFER            PIC X(100).
       01  WS-OUTPUT-FIELD            PIC X(30).
       01  WS-OVERFLOW-FLAG           PIC 9 VALUE 0.
       01  WS-STRING-PTR              PIC 99 VALUE 1.

      *    UNSAFE: Moving a larger field to a smaller one truncates
      *    silently. This may not cause an ABEND but can lead to
      *    data integrity issues.
      *    MOVE WS-INPUT-BUFFER TO WS-OUTPUT-FIELD

      *    SAFER: Use reference modification with length checking
       PROCEDURE DIVISION.
       1000-SAFE-STRING-MOVE.
      *    Determine actual data length (excluding trailing spaces)
           MOVE FUNCTION LENGTH(
               FUNCTION TRIM(WS-INPUT-BUFFER TRAILING))
               TO WS-DATA-LENGTH

      *    Check if it fits in the target field
           IF WS-DATA-LENGTH > FUNCTION LENGTH(WS-OUTPUT-FIELD)
               MOVE 1 TO WS-OVERFLOW-FLAG
               DISPLAY 'WARNING: DATA TRUNCATION OCCURRED'
                   UPON CONSOLE
      *        Move only what fits, explicitly
               MOVE WS-INPUT-BUFFER(1:FUNCTION LENGTH(
                   WS-OUTPUT-FIELD))
                   TO WS-OUTPUT-FIELD
           ELSE
               MOVE WS-INPUT-BUFFER TO WS-OUTPUT-FIELD
           END-IF.

       2000-SAFE-STRING-CONCATENATION.
      *    Using STRING with POINTER to prevent overflow
           MOVE 1 TO WS-STRING-PTR

           STRING WS-FIELD-1 DELIMITED SPACES
                  ' ' DELIMITED SIZE
                  WS-FIELD-2 DELIMITED SPACES
               INTO WS-OUTPUT-FIELD
               WITH POINTER WS-STRING-PTR
               ON OVERFLOW
                   MOVE 1 TO WS-OVERFLOW-FLAG
                   DISPLAY 'WARNING: STRING OVERFLOW DETECTED'
                       UPON CONSOLE
           END-STRING.

31.8.3 Sensitive Data Handling

COBOL programs in financial environments frequently handle sensitive data such as Social Security numbers, credit card numbers, and account balances. Proper handling of this data is both a security requirement and a compliance obligation:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. SENSDATA.
      *================================================================*
      * SENSITIVE DATA HANDLING MODULE                                  *
      * Demonstrates proper handling of PII and financial data          *
      * in compliance with PCI-DSS and SOX requirements.               *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-CUSTOMER-PII.
           05  WS-FULL-SSN            PIC X(9).
           05  WS-MASKED-SSN          PIC X(11).
           05  WS-CREDIT-CARD-NUM     PIC X(16).
           05  WS-MASKED-CC-NUM       PIC X(19).
           05  WS-ACCOUNT-PIN         PIC X(4).

       01  WS-CLEAR-PATTERN           PIC X(200) VALUE SPACES.

       PROCEDURE DIVISION.

       1000-MASK-SSN.
      *    Display only the last 4 digits of SSN
      *    Input:  WS-FULL-SSN = '123456789'
      *    Output: WS-MASKED-SSN = 'XXX-XX-6789'
           STRING 'XXX-XX-' DELIMITED SIZE
                  WS-FULL-SSN(6:4) DELIMITED SIZE
               INTO WS-MASKED-SSN
           END-STRING.

       2000-MASK-CREDIT-CARD.
      *    PCI-DSS requires masking all but the last 4 digits
      *    Input:  WS-CREDIT-CARD-NUM = '4111222233334444'
      *    Output: WS-MASKED-CC-NUM = 'XXXX-XXXX-XXXX-4444'
           STRING 'XXXX-XXXX-XXXX-' DELIMITED SIZE
                  WS-CREDIT-CARD-NUM(13:4) DELIMITED SIZE
               INTO WS-MASKED-CC-NUM
           END-STRING.

       3000-CLEAR-SENSITIVE-FIELDS.
      *    After processing is complete, clear all sensitive data
      *    from working storage to prevent residual data exposure.
      *    This is a PCI-DSS requirement.
           MOVE SPACES TO WS-FULL-SSN
           MOVE SPACES TO WS-CREDIT-CARD-NUM
           MOVE SPACES TO WS-ACCOUNT-PIN
           MOVE LOW-VALUES TO WS-CUSTOMER-PII.

       4000-LOG-DATA-ACCESS.
      *    Log access to sensitive data for audit trail
      *    NEVER log the actual sensitive data values
           DISPLAY 'AUDIT: USER=' WS-USERID
                   ' ACCESSED PII FOR CUSTOMER='
                   WS-CUSTOMER-ID
                   ' AT ' FUNCTION CURRENT-DATE
               UPON CONSOLE.
      *    NOTE: The actual SSN, credit card number, etc.
      *    are NEVER written to any log, report, or display.

Key Concept

Sensitive data must be masked for display, cleared from memory after use, and never written to logs or error messages. These practices are not just good security --- they are required by PCI-DSS, SOX, and other regulatory frameworks that govern financial systems.


31.9 Audit Trail Implementation in COBOL

Audit trails are the cornerstone of accountability in financial systems. Every significant action must be recorded with enough detail to answer the questions: who, what, when, where, and why. As we discussed in Chapter 22 regarding file handling and Chapter 24 regarding DB2, audit records can be written to sequential files, VSAM files, or DB2 tables.

31.9.1 Designing the Audit Record

A comprehensive audit record for a banking application should include:

       01  WS-AUDIT-RECORD.
      *    IDENTIFICATION FIELDS
           05  AUD-TIMESTAMP          PIC X(26).
           05  AUD-SYSTEM-ID          PIC X(4).
           05  AUD-REGION-TYPE        PIC X(5).
               88  AUD-BATCH          VALUE 'BATCH'.
               88  AUD-CICS           VALUE 'CICS '.
           05  AUD-JOB-NAME           PIC X(8).
           05  AUD-PROGRAM-NAME       PIC X(8).

      *    USER IDENTIFICATION
           05  AUD-USER-ID            PIC X(8).
           05  AUD-TERMINAL-ID        PIC X(4).
           05  AUD-TRANSACTION-ID     PIC X(4).

      *    ACTION DETAILS
           05  AUD-ACTION-TYPE        PIC X(3).
               88  AUD-READ           VALUE 'RD '.
               88  AUD-CREATE         VALUE 'CRT'.
               88  AUD-UPDATE         VALUE 'UPD'.
               88  AUD-DELETE         VALUE 'DEL'.
               88  AUD-SECURITY       VALUE 'SEC'.
               88  AUD-LOGIN          VALUE 'LGN'.
               88  AUD-LOGOUT         VALUE 'LGT'.
           05  AUD-RESOURCE-TYPE      PIC X(8).
           05  AUD-RESOURCE-NAME      PIC X(44).

      *    BUSINESS CONTEXT
           05  AUD-ACCOUNT-NUMBER     PIC X(10).
           05  AUD-CUSTOMER-ID        PIC X(10).
           05  AUD-AMOUNT             PIC S9(13)V99 COMP-3.
           05  AUD-DESCRIPTION        PIC X(50).

      *    RESULT
           05  AUD-RESULT-CODE        PIC X(4).
               88  AUD-SUCCESS        VALUE 'SUCC'.
               88  AUD-FAILURE        VALUE 'FAIL'.
               88  AUD-DENIED         VALUE 'DENY'.
           05  AUD-ERROR-CODE         PIC X(10).
           05  AUD-ERROR-DETAIL       PIC X(80).

      *    BEFORE/AFTER IMAGES FOR UPDATES
           05  AUD-BEFORE-IMAGE       PIC X(200).
           05  AUD-AFTER-IMAGE        PIC X(200).

31.9.2 Writing Audit Records to DB2

For most modern banking applications, DB2 is the preferred destination for audit records because it supports efficient querying and long-term retention:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. AUDITLOG.
      *================================================================*
      * AUDIT LOGGING MODULE                                            *
      * Writes audit records to the DB2 audit trail table.              *
      * This module is called from all application programs.            *
      *                                                                 *
      * Cross-reference: Chapter 24 (DB2 Programming),                  *
      *                  Chapter 26 (Subprogram Design)                 *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.
           EXEC SQL INCLUDE SQLCA END-EXEC.

       01  WS-AUDIT-SEQUENCE          PIC S9(18) COMP.
       01  WS-RETRY-COUNT             PIC 99 VALUE 0.
       01  WS-MAX-RETRIES             PIC 99 VALUE 3.

       LINKAGE SECTION.
       01  LS-AUDIT-RECORD.
           05  LS-AUD-TIMESTAMP       PIC X(26).
           05  LS-AUD-SYSTEM-ID       PIC X(4).
           05  LS-AUD-REGION-TYPE     PIC X(5).
           05  LS-AUD-JOB-NAME        PIC X(8).
           05  LS-AUD-PROGRAM-NAME    PIC X(8).
           05  LS-AUD-USER-ID         PIC X(8).
           05  LS-AUD-TERMINAL-ID     PIC X(4).
           05  LS-AUD-TRANSACTION-ID  PIC X(4).
           05  LS-AUD-ACTION-TYPE     PIC X(3).
           05  LS-AUD-RESOURCE-TYPE   PIC X(8).
           05  LS-AUD-RESOURCE-NAME   PIC X(44).
           05  LS-AUD-ACCOUNT-NUMBER  PIC X(10).
           05  LS-AUD-CUSTOMER-ID     PIC X(10).
           05  LS-AUD-AMOUNT          PIC S9(13)V99 COMP-3.
           05  LS-AUD-DESCRIPTION     PIC X(50).
           05  LS-AUD-RESULT-CODE     PIC X(4).
           05  LS-AUD-ERROR-CODE      PIC X(10).
           05  LS-AUD-ERROR-DETAIL    PIC X(80).

       01  LS-RETURN-CODE             PIC S9(4) COMP.

       PROCEDURE DIVISION USING LS-AUDIT-RECORD
                                LS-RETURN-CODE.
       0000-WRITE-AUDIT.
           MOVE 0 TO LS-RETURN-CODE
           MOVE 0 TO WS-RETRY-COUNT

           PERFORM 1000-INSERT-AUDIT-ROW
               UNTIL SQLCODE = 0
                  OR WS-RETRY-COUNT > WS-MAX-RETRIES

           IF SQLCODE NOT = 0
               MOVE 12 TO LS-RETURN-CODE
      *        If DB2 insert fails, write to a backup sequential
      *        file as a fallback to ensure no audit records are lost
               PERFORM 2000-WRITE-BACKUP-AUDIT
           END-IF

           GOBACK.

       1000-INSERT-AUDIT-ROW.
           EXEC SQL
               INSERT INTO BANKDB.AUDIT_TRAIL
               (AUDIT_TIMESTAMP,
                SYSTEM_ID,
                REGION_TYPE,
                JOB_NAME,
                PROGRAM_NAME,
                USER_ID,
                TERMINAL_ID,
                TRANSACTION_ID,
                ACTION_TYPE,
                RESOURCE_TYPE,
                RESOURCE_NAME,
                ACCOUNT_NUMBER,
                CUSTOMER_ID,
                AMOUNT,
                DESCRIPTION,
                RESULT_CODE,
                ERROR_CODE,
                ERROR_DETAIL)
               VALUES
               (:LS-AUD-TIMESTAMP,
                :LS-AUD-SYSTEM-ID,
                :LS-AUD-REGION-TYPE,
                :LS-AUD-JOB-NAME,
                :LS-AUD-PROGRAM-NAME,
                :LS-AUD-USER-ID,
                :LS-AUD-TERMINAL-ID,
                :LS-AUD-TRANSACTION-ID,
                :LS-AUD-ACTION-TYPE,
                :LS-AUD-RESOURCE-TYPE,
                :LS-AUD-RESOURCE-NAME,
                :LS-AUD-ACCOUNT-NUMBER,
                :LS-AUD-CUSTOMER-ID,
                :LS-AUD-AMOUNT,
                :LS-AUD-DESCRIPTION,
                :LS-AUD-RESULT-CODE,
                :LS-AUD-ERROR-CODE,
                :LS-AUD-ERROR-DETAIL)
           END-EXEC

           IF SQLCODE = -911
      *        Deadlock or timeout - retry
               ADD 1 TO WS-RETRY-COUNT
           END-IF.

       2000-WRITE-BACKUP-AUDIT.
      *    Fallback: write to the console and a backup file
      *    to ensure audit continuity even if DB2 is unavailable
           DISPLAY 'AUDIT BACKUP: ' LS-AUD-TIMESTAMP
                   ' USER=' LS-AUD-USER-ID
                   ' ACTION=' LS-AUD-ACTION-TYPE
                   ' RESOURCE=' LS-AUD-RESOURCE-NAME
                   ' RESULT=' LS-AUD-RESULT-CODE
               UPON CONSOLE.

31.9.3 Audit Trail for Security Events

Security events require special attention in the audit trail. These include login failures, authorization denials, privilege escalation attempts, and access to sensitive data:

       1000-LOG-SECURITY-EVENT.
      *    Set up the audit record for a security event
           MOVE FUNCTION CURRENT-DATE
               TO LS-AUD-TIMESTAMP
           MOVE 'SEC'          TO LS-AUD-ACTION-TYPE
           MOVE WS-USERID      TO LS-AUD-USER-ID
           MOVE WS-PROGRAM-ID  TO LS-AUD-PROGRAM-NAME
           MOVE 'DENY'         TO LS-AUD-RESULT-CODE

      *    Include the specific security failure detail
           EVALUATE WS-SECURITY-EVENT-TYPE
               WHEN 'AUTH-FAIL'
                   MOVE 'AUTHORIZATION FAILURE'
                       TO LS-AUD-DESCRIPTION
               WHEN 'PRIV-ESC'
                   MOVE 'PRIVILEGE ESCALATION ATTEMPT'
                       TO LS-AUD-DESCRIPTION
               WHEN 'ACCT-LOCK'
                   MOVE 'ACCOUNT LOCKED - EXCESSIVE FAILURES'
                       TO LS-AUD-DESCRIPTION
               WHEN 'DATA-ACC'
                   MOVE 'UNAUTHORIZED DATA ACCESS ATTEMPT'
                       TO LS-AUD-DESCRIPTION
           END-EVALUATE

           CALL 'AUDITLOG' USING LS-AUDIT-RECORD
                                  WS-AUDIT-RETURN-CODE.

Key Concept

An audit trail must be tamper-proof. The audit dataset or DB2 table should be protected so that application users have only INSERT access (no UPDATE or DELETE). The audit records themselves should never contain sensitive data values --- only references to the data that was accessed (such as account numbers or customer IDs, but not SSNs or account balances).


31.10 Compliance Requirements and COBOL Systems

Financial institutions running COBOL systems on z/OS must comply with a variety of regulatory frameworks. Two of the most significant are the Sarbanes-Oxley Act (SOX) and the Payment Card Industry Data Security Standard (PCI-DSS).

31.10.1 SOX Compliance

The Sarbanes-Oxley Act of 2002 was enacted in response to corporate financial scandals. For COBOL systems, the key SOX requirements include:

Section 302 --- Corporate Responsibility for Financial Reports: - Programs that generate financial reports must have controls to ensure accuracy. - Changes to financial reporting programs must go through a formal change management process.

Section 404 --- Assessment of Internal Controls: - Access controls around financial data must be documented and tested. - Audit trails must be maintained for all changes to financial data. - Segregation of duties must be enforced (developers cannot have production access).

SOX Controls in COBOL Programs:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. FINRPT01.
      *================================================================*
      * FINANCIAL REPORT GENERATOR                                      *
      * SOX Section 302/404 Compliance Controls:                        *
      *   - Report generation is logged in the audit trail              *
      *   - Report totals are cross-validated against control totals    *
      *   - A hash total is computed for integrity verification         *
      *   - The report run is recorded with a unique batch ID           *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-SOX-CONTROLS.
           05  WS-BATCH-RUN-ID        PIC X(20).
           05  WS-CONTROL-TOTAL       PIC S9(15)V99 COMP-3.
           05  WS-REPORT-TOTAL        PIC S9(15)V99 COMP-3.
           05  WS-RECORD-COUNT        PIC S9(9) COMP.
           05  WS-HASH-TOTAL          PIC S9(18) COMP.
           05  WS-RECONCILE-STATUS    PIC X(5).
               88  WS-RECONCILED      VALUE 'RECON'.
               88  WS-VARIANCE        VALUE 'VARNC'.

       01  WS-EXPECTED-CONTROL.
           05  WS-EXP-TOTAL           PIC S9(15)V99 COMP-3.
           05  WS-EXP-COUNT           PIC S9(9) COMP.

       PROCEDURE DIVISION.
       1000-INITIALIZE-SOX-CONTROLS.
      *    Generate a unique batch run ID for traceability
           STRING FUNCTION CURRENT-DATE(1:8)
                  '-'
                  FUNCTION CURRENT-DATE(9:6)
                  '-RPT01'
               DELIMITED SIZE
               INTO WS-BATCH-RUN-ID
           END-STRING

      *    Initialize control totals
           MOVE 0 TO WS-CONTROL-TOTAL
           MOVE 0 TO WS-REPORT-TOTAL
           MOVE 0 TO WS-RECORD-COUNT
           MOVE 0 TO WS-HASH-TOTAL

      *    Log the start of the report generation
           DISPLAY 'SOX AUDIT: REPORT GENERATION STARTED'
                   ' BATCH-ID=' WS-BATCH-RUN-ID
                   ' USER=' WS-USERID
                   ' TIMESTAMP=' FUNCTION CURRENT-DATE
               UPON CONSOLE.

       5000-RECONCILE-TOTALS.
      *    SOX Section 404: Validate report totals against
      *    independently computed control totals
           IF WS-REPORT-TOTAL = WS-EXP-TOTAL
              AND WS-RECORD-COUNT = WS-EXP-COUNT
               MOVE 'RECON' TO WS-RECONCILE-STATUS
               DISPLAY 'SOX AUDIT: RECONCILIATION SUCCESSFUL'
                       ' BATCH-ID=' WS-BATCH-RUN-ID
                       ' RECORDS=' WS-RECORD-COUNT
                   UPON CONSOLE
           ELSE
               MOVE 'VARNC' TO WS-RECONCILE-STATUS
               DISPLAY 'SOX ALERT: RECONCILIATION VARIANCE'
                       ' BATCH-ID=' WS-BATCH-RUN-ID
                       ' EXPECTED-TOTAL=' WS-EXP-TOTAL
                       ' ACTUAL-TOTAL=' WS-REPORT-TOTAL
                       ' EXPECTED-COUNT=' WS-EXP-COUNT
                       ' ACTUAL-COUNT=' WS-RECORD-COUNT
                   UPON CONSOLE
      *        A variance triggers an alert and the report
      *        must not be distributed until investigated
               MOVE 16 TO RETURN-CODE
           END-IF.

31.10.2 PCI-DSS Compliance

The Payment Card Industry Data Security Standard applies to any system that stores, processes, or transmits cardholder data. Many COBOL systems on z/OS process credit and debit card transactions, making PCI-DSS compliance critical.

Key PCI-DSS Requirements for COBOL Programs:

PCI-DSS Requirement Impact on COBOL Programs
Req 3: Protect stored cardholder data Encrypt or mask card numbers; limit data retention
Req 6: Develop and maintain secure systems Follow secure coding standards; perform code reviews
Req 7: Restrict access by business need Implement least-privilege access controls
Req 8: Identify and authenticate access Use unique user IDs; enforce password policies
Req 10: Track and monitor all access Implement comprehensive audit trails
Req 11: Test security systems regularly Conduct regular vulnerability assessments
       IDENTIFICATION DIVISION.
       PROGRAM-ID. PCIDSS.
      *================================================================*
      * PCI-DSS COMPLIANT CARD DATA HANDLER                             *
      * Implements PCI-DSS requirements for card data processing.       *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CARD-DATA.
      *    PCI Req 3.4: Render PAN unreadable anywhere it is stored
           05  WS-FULL-PAN            PIC X(19).
           05  WS-TRUNCATED-PAN       PIC X(19).
           05  WS-PAN-LAST-FOUR       PIC X(4).
           05  WS-TOKEN               PIC X(20).

       01  WS-RETENTION-DATA.
           05  WS-TRANS-DATE          PIC X(10).
           05  WS-CURRENT-DATE        PIC X(10).
           05  WS-DAYS-RETAINED       PIC S9(5) COMP.
           05  WS-MAX-RETENTION-DAYS  PIC S9(5) COMP VALUE 90.

       PROCEDURE DIVISION.

       1000-TOKENIZE-CARD-NUMBER.
      *    PCI Req 3.4: Replace the full PAN with a token
      *    The token is generated by a secure tokenization service
      *    and cannot be reversed without access to the token vault
           CALL 'TOKENSVC' USING WS-FULL-PAN
                                  WS-TOKEN

      *    Immediately clear the full PAN from memory
           MOVE SPACES TO WS-FULL-PAN

      *    Store only the token and the last four digits
           MOVE WS-FULL-PAN(16:4) TO WS-PAN-LAST-FOUR.

       2000-CREATE-TRUNCATED-PAN.
      *    PCI Req 3.3: Mask PAN when displayed
      *    Show only first 6 and last 4 digits (BIN + last 4)
           STRING WS-FULL-PAN(1:6)
                  'XXXXXXX'
                  WS-FULL-PAN(14:4)
               DELIMITED SIZE
               INTO WS-TRUNCATED-PAN
           END-STRING.

       3000-CHECK-DATA-RETENTION.
      *    PCI Req 3.1: Limit cardholder data retention
      *    Purge transaction data older than the retention period
           COMPUTE WS-DAYS-RETAINED =
               FUNCTION INTEGER-OF-DATE(
                   FUNCTION NUMVAL(WS-CURRENT-DATE))
             - FUNCTION INTEGER-OF-DATE(
                   FUNCTION NUMVAL(WS-TRANS-DATE))

           IF WS-DAYS-RETAINED > WS-MAX-RETENTION-DAYS
      *        This record must be purged
               PERFORM 3100-PURGE-CARD-DATA
           END-IF.

       3100-PURGE-CARD-DATA.
      *    Securely delete cardholder data that has exceeded
      *    the retention period. Replace with blanks and log
      *    the purge action for audit purposes.
           EXEC SQL
               UPDATE BANKDB.CARD_TRANSACTIONS
               SET    CARD_TOKEN = NULL,
                      CARD_LAST_FOUR = NULL,
                      PURGE_DATE = CURRENT DATE,
                      PURGE_REASON = 'PCI RETENTION LIMIT'
               WHERE  TRANSACTION_DATE < :WS-RETENTION-CUTOFF
               AND    CARD_TOKEN IS NOT NULL
           END-EXEC.

31.10.3 Implementing Segregation of Duties

Segregation of duties (SoD) is a fundamental control required by both SOX and PCI-DSS. In a COBOL mainframe environment, SoD is enforced through a combination of RACF access controls and application-level controls:

//*================================================================*
//* SEGREGATION OF DUTIES - RACF IMPLEMENTATION                     *
//* Developers cannot access production data.                       *
//* Production support cannot modify source code.                   *
//* Security administrators cannot run business transactions.       *
//*================================================================*
//SODSETUP JOB (ACCT),'SEC ADMIN',CLASS=A
//STEP1    EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  /* Development team - access to source, not production data */
  PERMIT 'BANK.PROD.ACCTMAST.**' ID(DEVTEAM) ACCESS(NONE)
  PERMIT 'BANK.SOURCE.**'        ID(DEVTEAM) ACCESS(UPDATE)
  PERMIT 'BANK.TEST.**'          ID(DEVTEAM) ACCESS(ALTER)

  /* Production support - access to production, not source */
  PERMIT 'BANK.PROD.**'          ID(PRODSUP) ACCESS(READ)
  PERMIT 'BANK.SOURCE.**'        ID(PRODSUP) ACCESS(NONE)

  /* Operations - can run jobs, not access data directly */
  PERMIT 'BANK.PROD.LOADLIB'     ID(OPSGRP) ACCESS(READ)
  PERMIT 'BANK.PROD.JCL'         ID(OPSGRP) ACCESS(READ)
  PERMIT 'BANK.PROD.ACCTMAST.**' ID(OPSGRP) ACCESS(NONE)
/*

31.11 Putting It All Together: A Secure COBOL Application

The following example brings together all of the security concepts discussed in this chapter into a complete, production-quality COBOL program. This program processes account balance adjustments in a banking environment, implementing security checks, input validation, audit logging, and compliance controls:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. BALADJ01.
      *================================================================*
      * BALANCE ADJUSTMENT PROGRAM                                      *
      * Processes authorized balance adjustments to customer accounts.   *
      *                                                                 *
      * Security Controls:                                              *
      *   - RACF dataset security via conditional access                *
      *   - DB2 authorization via plan OWNER                            *
      *   - Application-level authorization checking                    *
      *   - Full audit trail with before/after images                   *
      *   - Input validation on all fields                              *
      *   - Dual control for adjustments above threshold                *
      *   - PCI-DSS masking of sensitive data in logs                   *
      *                                                                 *
      * Cross-references:                                               *
      *   Chapter 22 - File Handling (VSAM access patterns)             *
      *   Chapter 23 - CICS Programming (transaction design)            *
      *   Chapter 24 - DB2 Programming (SQL best practices)             *
      *   Chapter 25 - Error Handling (ABEND management)                *
      *   Chapter 26 - Subprogram Design (modular architecture)         *
      *================================================================*

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT ADJUSTMENT-FILE
               ASSIGN TO ADJINPUT
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-ADJ-STATUS.
           SELECT AUDIT-FILE
               ASSIGN TO AUDITOUT
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-AUD-STATUS.
           SELECT REPORT-FILE
               ASSIGN TO RPTOUT
               FILE STATUS IS WS-RPT-STATUS.

       DATA DIVISION.
       FILE SECTION.
       FD  ADJUSTMENT-FILE
           RECORDING MODE IS F
           BLOCK CONTAINS 0 RECORDS.
       01  ADJUSTMENT-RECORD.
           05  ADJ-ACCOUNT-NUMBER     PIC X(10).
           05  ADJ-TYPE               PIC X(2).
               88  ADJ-CREDIT         VALUE 'CR'.
               88  ADJ-DEBIT          VALUE 'DB'.
           05  ADJ-AMOUNT             PIC S9(11)V99 COMP-3.
           05  ADJ-REASON-CODE        PIC X(4).
           05  ADJ-AUTHORIZED-BY      PIC X(8).
           05  ADJ-APPROVED-BY        PIC X(8).
           05  ADJ-REFERENCE          PIC X(30).
           05  FILLER                 PIC X(16).

       FD  AUDIT-FILE
           RECORDING MODE IS F
           BLOCK CONTAINS 0 RECORDS.
       01  AUDIT-OUTPUT-RECORD        PIC X(500).

       FD  REPORT-FILE
           RECORDING MODE IS FA
           BLOCK CONTAINS 0 RECORDS.
       01  REPORT-RECORD              PIC X(133).

       WORKING-STORAGE SECTION.
           EXEC SQL INCLUDE SQLCA END-EXEC.

       01  WS-FILE-STATUSES.
           05  WS-ADJ-STATUS          PIC XX.
           05  WS-AUD-STATUS          PIC XX.
           05  WS-RPT-STATUS          PIC XX.

       01  WS-CONTROL-FIELDS.
           05  WS-EOF-FLAG            PIC 9 VALUE 0.
               88  WS-END-OF-FILE     VALUE 1.
           05  WS-RETURN-CODE         PIC S9(4) COMP VALUE 0.
           05  WS-RECORD-COUNT        PIC S9(9) COMP VALUE 0.
           05  WS-SUCCESS-COUNT       PIC S9(9) COMP VALUE 0.
           05  WS-REJECT-COUNT        PIC S9(9) COMP VALUE 0.
           05  WS-ERROR-COUNT         PIC S9(9) COMP VALUE 0.
           05  WS-DUAL-CTRL-THRESH    PIC S9(11)V99 COMP-3
                                         VALUE 10000.00.

       01  WS-DB2-ACCOUNT.
           05  WS-DB-ACCT-NUM         PIC X(10).
           05  WS-DB-ACCT-NAME        PIC X(30).
           05  WS-DB-ACCT-BAL         PIC S9(13)V99 COMP-3.
           05  WS-DB-ACCT-STATUS      PIC X(1).
           05  WS-DB-LAST-ADJ-DATE    PIC X(10).

       01  WS-BEFORE-BALANCE          PIC S9(13)V99 COMP-3.
       01  WS-AFTER-BALANCE           PIC S9(13)V99 COMP-3.

       01  WS-AUDIT-RECORD.
           05  AUD-TIMESTAMP          PIC X(26).
           05  AUD-PROGRAM            PIC X(8) VALUE 'BALADJ01'.
           05  AUD-ACTION             PIC X(10).
           05  AUD-ACCOUNT            PIC X(10).
           05  AUD-USER               PIC X(8).
           05  AUD-APPROVER           PIC X(8).
           05  AUD-AMOUNT             PIC S9(11)V99 COMP-3.
           05  AUD-BEFORE-BAL         PIC S9(13)V99 COMP-3.
           05  AUD-AFTER-BAL          PIC S9(13)V99 COMP-3.
           05  AUD-RESULT             PIC X(8).
           05  AUD-REASON             PIC X(80).

       01  WS-VALIDATION-MSG          PIC X(80).
       01  WS-VALID-FLAG              PIC 9 VALUE 0.
           88  WS-RECORD-VALID        VALUE 1.
           88  WS-RECORD-INVALID      VALUE 0.

       PROCEDURE DIVISION.
       0000-MAIN-LOGIC.
           PERFORM 1000-INITIALIZE
           IF WS-RETURN-CODE = 0
               PERFORM 2000-PROCESS-ADJUSTMENTS
                   UNTIL WS-END-OF-FILE
           END-IF
           PERFORM 9000-TERMINATE
           MOVE WS-RETURN-CODE TO RETURN-CODE
           STOP RUN.

       1000-INITIALIZE.
           OPEN INPUT  ADJUSTMENT-FILE
           IF WS-ADJ-STATUS NOT = '00'
               DISPLAY 'FATAL: CANNOT OPEN ADJUSTMENT FILE. '
                       'STATUS=' WS-ADJ-STATUS UPON CONSOLE
               IF WS-ADJ-STATUS = '90' OR '93'
                   DISPLAY 'SECURITY ERROR - CONTACT ADMIN'
                       UPON CONSOLE
               END-IF
               MOVE 16 TO WS-RETURN-CODE
           END-IF

           IF WS-RETURN-CODE = 0
               OPEN OUTPUT AUDIT-FILE
               IF WS-AUD-STATUS NOT = '00'
                   DISPLAY 'FATAL: CANNOT OPEN AUDIT FILE. '
                           'STATUS=' WS-AUD-STATUS UPON CONSOLE
                   MOVE 16 TO WS-RETURN-CODE
               END-IF
           END-IF

           IF WS-RETURN-CODE = 0
               OPEN OUTPUT REPORT-FILE
               IF WS-RPT-STATUS NOT = '00'
                   DISPLAY 'FATAL: CANNOT OPEN REPORT FILE. '
                           'STATUS=' WS-RPT-STATUS UPON CONSOLE
                   MOVE 16 TO WS-RETURN-CODE
               END-IF
           END-IF

           IF WS-RETURN-CODE = 0
               PERFORM 2100-READ-ADJUSTMENT
           END-IF.

       2000-PROCESS-ADJUSTMENTS.
           ADD 1 TO WS-RECORD-COUNT

      *    Step 1: Validate the input record
           PERFORM 3000-VALIDATE-ADJUSTMENT

           IF WS-RECORD-VALID
      *        Step 2: Verify dual control for large adjustments
               PERFORM 4000-CHECK-DUAL-CONTROL
           END-IF

           IF WS-RECORD-VALID
      *        Step 3: Read the current account balance
               PERFORM 5000-READ-ACCOUNT
           END-IF

           IF WS-RECORD-VALID
      *        Step 4: Apply the adjustment
               PERFORM 6000-APPLY-ADJUSTMENT
           END-IF

      *    Step 5: Write the audit record regardless of outcome
           PERFORM 7000-WRITE-AUDIT

      *    Read the next input record
           PERFORM 2100-READ-ADJUSTMENT.

       2100-READ-ADJUSTMENT.
           READ ADJUSTMENT-FILE
               AT END MOVE 1 TO WS-EOF-FLAG
           END-READ.

       3000-VALIDATE-ADJUSTMENT.
           MOVE 1 TO WS-VALID-FLAG

      *    Validate account number
           IF ADJ-ACCOUNT-NUMBER IS NOT NUMERIC
              OR ADJ-ACCOUNT-NUMBER = SPACES
               MOVE 0 TO WS-VALID-FLAG
               MOVE 'INVALID ACCOUNT NUMBER'
                   TO WS-VALIDATION-MSG
               ADD 1 TO WS-REJECT-COUNT
           END-IF

      *    Validate adjustment type
           IF WS-RECORD-VALID
               IF NOT (ADJ-CREDIT OR ADJ-DEBIT)
                   MOVE 0 TO WS-VALID-FLAG
                   MOVE 'INVALID ADJUSTMENT TYPE'
                       TO WS-VALIDATION-MSG
                   ADD 1 TO WS-REJECT-COUNT
               END-IF
           END-IF

      *    Validate amount is positive and reasonable
           IF WS-RECORD-VALID
               IF ADJ-AMOUNT <= 0
                  OR ADJ-AMOUNT > 999999999.99
                   MOVE 0 TO WS-VALID-FLAG
                   MOVE 'INVALID ADJUSTMENT AMOUNT'
                       TO WS-VALIDATION-MSG
                   ADD 1 TO WS-REJECT-COUNT
               END-IF
           END-IF

      *    Validate authorized-by is not blank
           IF WS-RECORD-VALID
               IF ADJ-AUTHORIZED-BY = SPACES
                   MOVE 0 TO WS-VALID-FLAG
                   MOVE 'MISSING AUTHORIZATION USER ID'
                       TO WS-VALIDATION-MSG
                   ADD 1 TO WS-REJECT-COUNT
               END-IF
           END-IF.

       4000-CHECK-DUAL-CONTROL.
      *    SOX/Compliance: Adjustments above threshold require
      *    dual control (two different authorized individuals)
           IF ADJ-AMOUNT > WS-DUAL-CTRL-THRESH
               IF ADJ-APPROVED-BY = SPACES
                   MOVE 0 TO WS-VALID-FLAG
                   MOVE 'DUAL CONTROL REQUIRED - MISSING APPROVER'
                       TO WS-VALIDATION-MSG
                   ADD 1 TO WS-REJECT-COUNT
               ELSE
                   IF ADJ-APPROVED-BY = ADJ-AUTHORIZED-BY
                       MOVE 0 TO WS-VALID-FLAG
                       MOVE 'DUAL CONTROL VIOLATION - '
                         &  'SAME USER AUTHORIZED AND APPROVED'
                           TO WS-VALIDATION-MSG
                       ADD 1 TO WS-REJECT-COUNT
      *                Log this as a potential fraud attempt
                       DISPLAY 'SECURITY ALERT: DUAL CONTROL '
                               'VIOLATION BY USER '
                               ADJ-AUTHORIZED-BY
                               ' ON ACCOUNT '
                               ADJ-ACCOUNT-NUMBER
                           UPON CONSOLE
                   END-IF
               END-IF
           END-IF.

       5000-READ-ACCOUNT.
           EXEC SQL
               SELECT ACCT_NUMBER,
                      ACCT_NAME,
                      ACCT_BALANCE,
                      ACCT_STATUS,
                      LAST_ADJUSTMENT_DATE
               INTO   :WS-DB-ACCT-NUM,
                      :WS-DB-ACCT-NAME,
                      :WS-DB-ACCT-BAL,
                      :WS-DB-ACCT-STATUS,
                      :WS-DB-LAST-ADJ-DATE
               FROM   BANKDB.ACCT_MASTER
               WHERE  ACCT_NUMBER = :ADJ-ACCOUNT-NUMBER
           END-EXEC

           EVALUATE SQLCODE
               WHEN 0
                   MOVE WS-DB-ACCT-BAL TO WS-BEFORE-BALANCE
                   IF WS-DB-ACCT-STATUS NOT = 'A'
                       MOVE 0 TO WS-VALID-FLAG
                       MOVE 'ACCOUNT IS NOT ACTIVE'
                           TO WS-VALIDATION-MSG
                       ADD 1 TO WS-REJECT-COUNT
                   END-IF
               WHEN 100
                   MOVE 0 TO WS-VALID-FLAG
                   MOVE 'ACCOUNT NOT FOUND' TO WS-VALIDATION-MSG
                   ADD 1 TO WS-REJECT-COUNT
               WHEN -551
                   MOVE 0 TO WS-VALID-FLAG
                   MOVE 'DB2 SECURITY ERROR - NOT AUTHORIZED'
                       TO WS-VALIDATION-MSG
                   ADD 1 TO WS-ERROR-COUNT
                   MOVE 16 TO WS-RETURN-CODE
               WHEN OTHER
                   MOVE 0 TO WS-VALID-FLAG
                   MOVE 'DB2 ERROR READING ACCOUNT'
                       TO WS-VALIDATION-MSG
                   ADD 1 TO WS-ERROR-COUNT
           END-EVALUATE.

       6000-APPLY-ADJUSTMENT.
      *    Compute the new balance
           IF ADJ-CREDIT
               ADD ADJ-AMOUNT TO WS-DB-ACCT-BAL
                   GIVING WS-AFTER-BALANCE
           ELSE
               SUBTRACT ADJ-AMOUNT FROM WS-DB-ACCT-BAL
                   GIVING WS-AFTER-BALANCE
           END-IF

      *    Update the account in DB2
           EXEC SQL
               UPDATE BANKDB.ACCT_MASTER
               SET    ACCT_BALANCE = :WS-AFTER-BALANCE,
                      LAST_ADJUSTMENT_DATE = CURRENT DATE
               WHERE  ACCT_NUMBER = :ADJ-ACCOUNT-NUMBER
               AND    ACCT_STATUS = 'A'
           END-EXEC

           EVALUATE SQLCODE
               WHEN 0
                   ADD 1 TO WS-SUCCESS-COUNT
                   MOVE 'SUCC' TO AUD-RESULT
               WHEN -551
                   MOVE 'DENY' TO AUD-RESULT
                   ADD 1 TO WS-ERROR-COUNT
                   MOVE 16 TO WS-RETURN-CODE
               WHEN OTHER
                   MOVE 'FAIL' TO AUD-RESULT
                   ADD 1 TO WS-ERROR-COUNT
           END-EVALUATE.

       7000-WRITE-AUDIT.
           MOVE FUNCTION CURRENT-DATE TO AUD-TIMESTAMP
           MOVE ADJ-ACCOUNT-NUMBER    TO AUD-ACCOUNT
           MOVE ADJ-AUTHORIZED-BY     TO AUD-USER
           MOVE ADJ-APPROVED-BY       TO AUD-APPROVER
           MOVE ADJ-AMOUNT            TO AUD-AMOUNT
           MOVE WS-BEFORE-BALANCE     TO AUD-BEFORE-BAL
           MOVE WS-AFTER-BALANCE      TO AUD-AFTER-BAL

           IF WS-RECORD-VALID
               MOVE 'ADJUSTMENT' TO AUD-ACTION
           ELSE
               MOVE 'REJECTED' TO AUD-ACTION
               MOVE 'FAIL' TO AUD-RESULT
               MOVE WS-VALIDATION-MSG TO AUD-REASON
           END-IF

           MOVE WS-AUDIT-RECORD TO AUDIT-OUTPUT-RECORD
           WRITE AUDIT-OUTPUT-RECORD.

       9000-TERMINATE.
           CLOSE ADJUSTMENT-FILE
                 AUDIT-FILE
                 REPORT-FILE

           DISPLAY 'BALADJ01 PROCESSING COMPLETE' UPON CONSOLE
           DISPLAY '  RECORDS PROCESSED: ' WS-RECORD-COUNT
               UPON CONSOLE
           DISPLAY '  SUCCESSFUL:        ' WS-SUCCESS-COUNT
               UPON CONSOLE
           DISPLAY '  REJECTED:          ' WS-REJECT-COUNT
               UPON CONSOLE
           DISPLAY '  ERRORS:            ' WS-ERROR-COUNT
               UPON CONSOLE.

The JCL to run this program with appropriate security context:

//BALADJ   JOB (BANK,ADJ),'BALANCE ADJUSTMENTS',
//             CLASS=A,MSGCLASS=X,MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID,
//             TYPRUN=SCAN
//*================================================================*
//* BALANCE ADJUSTMENT PROCESSING                                   *
//* Security Requirements:                                          *
//*   - User must be in ADJPROC RACF group                          *
//*   - Program BALADJ01 must be defined in PROGRAM class           *
//*   - Conditional access to BANK.PROD.ACCTMAST via BALADJ01      *
//*   - EXECUTE privilege on DB2 plan BALADJ01                      *
//*   - UPDATE access to BANK.PROD.AUDITLOG                        *
//*                                                                 *
//* SOX Controls:                                                   *
//*   - Job submission restricted to ADJPROC group                  *
//*   - Dual control enforced for amounts > $10,000                 *
//*   - Full audit trail with before/after images                   *
//*   - Reconciliation totals produced at end of job                *
//*================================================================*
//*
//ADJSTEP  EXEC PGM=IKJEFT01,DYNAMNBR=20
//STEPLIB  DD DSN=BANK.PROD.LOADLIB,DISP=SHR
//         DD DSN=DSN.V13R1.SDSNLOAD,DISP=SHR
//ADJINPUT DD DSN=BANK.PROD.ADJUST.DAILY,DISP=SHR
//AUDITOUT DD DSN=BANK.PROD.AUDITLOG(+1),
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(5,2),RLSE),
//            DCB=(RECFM=FB,LRECL=500,BLKSIZE=27500)
//RPTOUT   DD SYSOUT=*,DEST=LOCAL
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  DSN SYSTEM(DB2P)
  RUN PROGRAM(BALADJ01) PLAN(BALADJ01) -
      LIB('BANK.PROD.LOADLIB')
  END
/*

31.12 Summary

Security is not a feature that can be added to a COBOL program after the fact --- it must be woven into the fabric of every program from the design phase through implementation and maintenance. In this chapter, we have covered:

  • z/OS security architecture --- The layered approach of SAF, ESMs (RACF, ACF2, Top Secret), and how they interact with COBOL programs.
  • RACF fundamentals --- Users, groups, profiles, and the five access levels (NONE, READ, UPDATE, CONTROL, ALTER) that control resource access.
  • Dataset security --- Discrete and generic profiles, conditional access, and how to handle security failures in COBOL batch programs.
  • Program security --- Program-controlled access, APF authorization, and program integrity controls.
  • CICS security --- Transaction security, resource security, COMMAREA security considerations, and how to use EXEC CICS QUERY SECURITY.
  • DB2 security --- The GRANT/REVOKE model, plan and package authorization, and handling DB2 security SQLCODEs.
  • Secure coding practices --- Input validation, buffer overflow prevention, and sensitive data handling.
  • Audit trail implementation --- Designing comprehensive audit records and writing them to DB2 with backup fallback.
  • Compliance requirements --- SOX Sections 302 and 404, PCI-DSS requirements, and segregation of duties as they apply to COBOL systems.

As you continue through Part VI, the security concepts in this chapter will be essential context for understanding performance tuning (Chapter 32), where security overhead must be balanced against performance requirements, and for the batch processing patterns discussed throughout this section. The techniques presented here are in daily use at thousands of financial institutions worldwide, protecting trillions of dollars in assets processed by COBOL programs running on z/OS.


Chapter Review Questions:

  1. What are the five RACF access levels, and when would each be appropriate for a COBOL application?
  2. Explain the difference between discrete and generic RACF profiles. Why are generic profiles preferred in production?
  3. How does conditional access (WHEN PROGRAM) provide defense in depth for dataset security?
  4. What SQLCODE values indicate DB2 authorization failures, and how should a COBOL program respond to each?
  5. Describe three PCI-DSS requirements that directly affect how COBOL programs handle credit card data.
  6. What is dual control, and why is it important for SOX compliance in a balance adjustment application?
  7. Why should sensitive data be cleared from WORKING-STORAGE after processing, and how would you implement this?
  8. Design an audit record structure for a loan origination system that satisfies both SOX and PCI-DSS requirements.