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...
In This Chapter
- 31.1 z/OS Security Overview
- 31.2 RACF Fundamentals
- 31.3 Dataset Security
- 31.4 Program Security
- 31.5 CICS Security
- 31.6 DB2 Security
- 31.7 Security in COBOL Programs: Handling Security Errors
- 31.8 Secure Coding Practices
- 31.9 Audit Trail Implementation in COBOL
- 31.10 Compliance Requirements and COBOL Systems
- 31.11 Putting It All Together: A Secure COBOL Application
- 31.12 Summary
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:
- Identification --- Every user and process must have a verifiable identity.
- Authentication --- That identity must be proven, typically through a password, passphrase, certificate, or multi-factor authentication token.
- 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:
- The program must be defined in the RACF PROGRAM class.
- The program library must be APF-authorized or the program must be in a controlled library.
- 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:
- What are the five RACF access levels, and when would each be appropriate for a COBOL application?
- Explain the difference between discrete and generic RACF profiles. Why are generic profiles preferred in production?
- How does conditional access (WHEN PROGRAM) provide defense in depth for dataset security?
- What SQLCODE values indicate DB2 authorization failures, and how should a COBOL program respond to each?
- Describe three PCI-DSS requirements that directly affect how COBOL programs handle credit card data.
- What is dual control, and why is it important for SOX compliance in a balance adjustment application?
- Why should sensitive data be cleared from WORKING-STORAGE after processing, and how would you implement this?
- Design an audit record structure for a loan origination system that satisfies both SOX and PCI-DSS requirements.