IBM Support

Restricting CICS TG client applications

Technical Blog Post


Abstract

Restricting CICS TG client applications

Body

In CICS Transaction Gateway (CICS TG) V9 we fulfilled 17 customer requirements which had been raised in RFE. The full list can be seen in the document entitled "Customer requirements answered in CICS Transaction Gateway V9.0" but in this article we will look at 22036 CICS Request Exit to provide client IP address and how it can be used to control who is able to call CICS Programs.

This is a question that I've heard from customers where they want to ensure that the client applications that are connected to CICS TG are from known IP addresses or a particular subnet. In this post I'll show how the CICS Request Exit functionality provided with the latest version of CICS TG can provide that functionality.

The CICS Request Exit allows for a supplied Java™ class to be loaded and run as part of the transaction processing done by the Gateway daemon. The exit is called at the start of every transaction and allows us to change where the transaction is run or prevent the transaction from happening at all. To write a CICS Request Exit we need to create a Java class which implements the CICSRequestExit interface. The skeleton for this is:
 

package com.ibm.ctg.samples.clientblocker;    
import java.util.Map;  import com.ibm.ctg.ha.*;    
public class ClientBlocker implements CICSRequestExit {    }


The interface defines three methods:

  public String getCICSServer();  
  public int getRetryCount();
  public void eventFired();


The getCICSServer method is the one that is called for each transaction, so is where we add the logic to reject a client's request based on their IP address. The code for this is:

    public List<String> allowList = Arrays.asList("127.0.0.1");    
    public String getCICSServer(Map<RequestDetails, Object> details)           throws InvalidRequestException {  
    	   String server = details.get(RequestDetails.Server).toString();
    	   String clientIP = ((InetAddress) details.get(RequestDetails.ClientLocation)).getHostAddress();
    	   if (!allowList.contains(clientIP)) { 
    		      throw new InvalidRequestException("Client not allowed to call CICS");
    		   }     return server; 
    }


We define a list of IP addresses that are allowed to run client applications and then for each transaction check whether the IP address of the client application is in the list. If it is, then the method returns the name of the CICS server the request originally requested to go to. If the client application's IP address is not found in the list then the method throws an InvalidRequestException which tells the Gateway daemon that the request should not be processed. When this happens the client application receives the error code ECI_ERR_INVALID_CALL_TYPE.

The getRetryCount method is used when an exit wants to try and route a request to an alternative CICS server should the attempt to the initial CICS server fail. As this exit is only deciding whether a request should proceed or not then we can leave this with a hard coded value of 0.

public int getRetryCount() {     return 0;  }


The final method we have to implement is the eventFired method. This method is called once when the Gateway daemon is shutting down, and every time a command is sent through using a SDSF modify command or the ctgadmin tool. In our exit we can use this ability to dynamically add or remove IP addresses to the list and write the current list to the standard output stream. Our implementation will accept commands of the following format:

z/OS

  /F <JOBNAME>,APPL=CREXIT,CMD=ADD=10.0.11.3  /F <JOBNAME>,APPL=CREXIT,CMD=DEL=127.0.0.1  /F <JOBNAME>,APPL=CREXIT,CMD=LIST


Multi-platforms

  ctgadmin -a crexit -cmd add=10.0.11.3  ctgadmin -a crexit -cmd del=127.0.0.1  ctgadmin -a crexit -cmd list


The code for the method is as follows:

   public void eventFired(ExitEvent event, Map<ExitEventData, Object> eventData) {
    	  switch (event) { 
    	  case Command:      
    	 String commandData = eventData.get(ExitEventData.CommandData).toString();     
    	   if (commandData.startsWith("ADD")) { 
    		        int location = commandData.indexOf("="); 
    		        if (location > 0) { 
    			         ArrayList<String> newAllowList = new ArrayList<String>(allowList); 
    			           newAllowList.add(commandData.substring(location + 1)); 
    			           allowList = newAllowList; 
    			        }    
    		   } else if (commandData.startsWith("DEL")) { 
    			        int location = commandData.indexOf("=");
    			        if (location > 0) {
    				           ArrayList<String> newAllowList = new ArrayList<String>(allowList); 
    				           newAllowList.remove(commandData.substring(location + 1)); 
    				           allowList = newAllowList;      
    				    }     
    			   } else if (commandData.startsWith("LIST")) {
    				        StringBuffer list = new StringBuffer("Allowed IP addresses:"); 
    				        for (String ip : allowList) { 
    					           list.append("\n\t");  
    					           list.append(ip);
    					        }          
    				System.out.println(list); 
    					     }       
    	 break;
    					  case ShutDown:        // do nothing        break;     }
    	}
    }
    
    
    


In order to process the command information, the code retrieves the supplied command from the Map of ExitEventData and then processes it. The code looks at the beginning of the String to see which command to run and then takes any characters after the equals sign as an IP address for the ADD and DEL commands.

Putting all these methods together into one class provides us with our exit. The configuration file needs to be updated with the following line being added to the SECTION GATEWAY part of the configuration file:

  cicsrequestexit=com.ibm.ctg.samples.clientblocker.ClientBlocker


When starting the Gateway daemon, if everything is working the following log message is written out:

  CTG8447I CICS Request exit com.ibm.ctg.samples.clientblocker.ClientBlocker installed successfully 


If there was a problem loading the exit or installing it then an error message is written out instead indicating the cause of the failure.

With everything up and running my CICS Transaction Gateway will now only accept work from client applications connecting from IP addresses I have authorized. With some minor changes to the code this exit could be made to allow all IP addresses and explicitly disallow certain addresses by changing the logic in the getCICSServer method to throw the Exception if the list contains the IP address rather than if it doesn't.

Using this new piece of information provided to the CICS Request Exit adds a whole range of function to the CICS Transaction Gateway in just a few lines of Java code at the control of the administrator.

Our normal disclaimer applies to all code samples.

[{"Business Unit":{"code":"BU058","label":"IBM Infrastructure w\/TPS"},"Product":{"code":"SSGMGV","label":"CICS Transaction Server"},"Component":"","Platform":[{"code":"PF035","label":"z\/OS"}],"Version":"5.2;5.3;5.4;5.5","Edition":"","Line of Business":{"code":"LOB35","label":"Mainframe SW"}}]

UID

ibm11080825