The security manager provides a special environment that is safe even if agent programs try to perform unexpected actions. The security manager is called if an agent program tries to:
Call an external function
Use a host command
Use the ::REQUIRES directive
Access the .LOCAL directory
Access the .ENVIRONMENT directory
Use a stream name in the input and output built-in functions (CHARIN, CHAROUT, CHARS, LINEIN, LINEOUT, LINES, and STREAM)
When the language processor reaches any of the defined security checkpoints, it sends a message to the security manager for the particular checkpoint. The message has a single argument, a directory of information that pertains to the checkpoint. If the security manager chooses to handle the action instead of the language processor, the security manager uses the checkpoint information directory to pass information back to the language processor.
Security manager methods must return a value of either 0 or 1 to the language processor. A value of 0 indicates that the program is authorized to perform the indicated action. In this case, processing continues as usual. A value of 1 indicates that the security manager performed the action itself. The security manager sets entries in the information directory to pass results for the action back to the language processor. The security manager can also use the RAISE instruction to raise a program error for a prohibited access. Error message 98.948 indicates authorization failures.
The defined checkpoints, with their arguments and return values, are:
sent for all external function calls. The information directory contains the following entries:
The name of the invoked function.
An array of the function arguments.
When the CALL method returns 1, indicating that it handled the external call, the security manager places the function result in the information directory as the entry RESULT.
sent for all host command instructions. The information directory contains the following entries:
The string that represents the host command.
The name of the target ADDRESS environment for the command.
When the COMMAND method returns 1, indicating that it handled the command, the security manager uses the following information directory entries to return the command results:
The command return code. If the entry is not set, a return code of 0 is used.
If a FAILURE entry is added to the information directory, a Rexx FAILURE condition is raised.
If an ERROR entry is added to the information directory, a Rexx ERROR condition is raised. The ERROR condition is raised only if the FAILURE entry is not set.
sent whenever a ::REQUIRES directive in the file is processed. The information directory contains the following entry:
The name of the file specified on the ::REQUIRES directive.
sent whenever Rexx is going to access an entry in the .LOCAL directory as part of the resolution of the environment symbol name. The information directory contains the following entry:
The name of the target directory entry.
sent whenever Rexx is going to access an entry in the .ENVIRONMENT directory as part of the resolution of the environment symbol name. The information directory contains the following entry:
The name of the target directory entry.
sent whenever one of the Rexx input and output built-in functions (CHARIN, CHAROUT, CHARS, LINEIN, LINEOUT, LINES, or STREAM) needs to resolve a stream name. The information directory contains the following entry:
The name of the target stream.
sent whenever a secure program attempts to send a message for a protected method (see the ::METHOD directive ::METHOD) to an object. The information directory contains the following entries:
The object the protected method is issued against.
The name of the protected method.
An array containing the method arguments.
The following agent program includes all the actions for which the security manager defines checkpoints (for example, by calling an external function).
Figure 13-1. Agent Program
/* Agent */ interpret "echo Hello There" "dir foo.bar" call rxfuncadd sysloadfuncs, rexxutil, sysloadfuncs say result say syssleep(1) say linein("c:\profile") say .array .object~setmethod("SETMETHOD") ::requires agent2.cmd
The following server implements the security manager with three levels of security. For each action the security manager must check (for example, by calling an external routine):
The audit manager (Dumper class) writes a record of the event but then permits the action.
The closed cell manager (noWay class) does not permit the action to take place and raises an error.
The replacement execution environment (Replacer class, a subclass of the noWay class) replaces the prohibited action with a different action.
Figure 13-2. Example of Server Implementing Security Manager
/* Server implements security manager */ parse arg program method = .method~newfile(program) say "Calling program" program "with an audit manager:" pull method~setSecurityManager(.dumper~new(.output)) .go~new~~run(method) say "Calling program" program "with a function replacement execution environment:" pull method~setSecurityManager(.replacer~new) .go~new~~run(method) say "Calling program" program "with a closed cell manager:" pull signal on syntax method~setSecurityManager(.noWay~new) .go~new~~run(method) exit syntax: say "Agent program terminated with an authorization failure" exit ::class go subclass object ::method run -- this is a NON-PRIVATE method! use arg m self~run:super(m) -- a PRIVATE method is called here! ::class dumper ::method init expose stream /* target stream for output */ use arg stream /* hook up the output stream */ ::method unknown /* generic unknown method */ expose stream /* need the global stream */ use arg name, args /* get the message and arguments */ /* write out the audit event */ stream~lineout(time() date() "Called for event" name) stream~lineout("Arguments are:") /* write out the arguments */ info = args[1] /* info directory is the first arg */ do name over info /* dump the info directory */ stream~lineout("Item" name":" info[name]) end return 0 /* allow this to proceed */ ::class noWay ::method unknown /* everything trapped by unknown */ /* and everything is an error */ raise syntax 98.948 array("You didn't say the magic word!") ::class replacer subclass noWay /* inherit restrictive UNKNOWN method*/ ::method command /* issuing commands */ use arg info /* access the directory */ info~rc = 1234 /* set the command return code */ info~failure = .true /* raise a FAILURE condition */ return 1 /* return "handled" return value */ ::method call /* external function/routine call */ use arg info /* access the directory */ /* all results are the same */ info~setentry("RESULT","uh, uh, uh...you didn't say the magic word") return 1 /* return "handled" return value */ ::method stream /* I/O function stream lookup */ use arg info /* access the directory */ /* replace with a different stream */ info~stream = .stream~new("c:\sample.txt") return 1 /* return "handled" return value */ ::method local /* .LOCAL variable lookup */ /* no value returned at all */ return 1 /* return "handled" return value */ ::method environment /* .ENVIRONMENT variable lookup */ /* no value returned at all */ return 1 /* return "handled" return value */ ::method method /* protected method invocation */ use arg info /* access the directory */ /* all results are the same */ info~setentry("RESULT","uh, uh, uh...you didn't say the magic word") return 1 /* return "handled" return value */ ::method requires /* REQUIRES directive */ use arg info /* access the directory */ /* switch to load a different file */ info~name = "c:\samples\agent.cmd" info~securitymanager = self /* load under this authority */ return 1 /* return "handled" return value */