Chapter 13. The Security Manager

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:

Calls to the Security Manager

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:

CALL

sent for all external function calls. The information directory contains the following entries:

NAME

The name of the invoked function.

ARGUMENTS

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.

COMMAND

sent for all host command instructions. The information directory contains the following entries:

COMMAND

The string that represents the host command.

ADDRESS

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:

RC

The command return code. If the entry is not set, a return code of 0 is used.

FAILURE

If a FAILURE entry is added to the information directory, a Rexx FAILURE condition is raised.

ERROR

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.

REQUIRES

sent whenever a ::REQUIRES directive in the file is processed. The information directory contains the following entry:

NAME

The name of the file specified on the ::REQUIRES directive.

When the REQUIRES method returns 1, indicating that it handled the request, the entry NAME in the information directory is replaced with the name of the actual file to load for the request. The REQUIRES method can also provide a security manager to be used for the program loaded by the ::REQUIRES directive by setting the information direction entry SECURITYMANAGER into the desired security manager object.

LOCAL

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:

NAME

The name of the target directory entry.

When the LOCAL method returns 1, indicating that it handled the request, the information directory entry RESULT contains the directory entry. When RESULT is not set and the method returns 1, this is the same as a failure to find an entry in the .LOCAL directory. Rexx continues with the next step in the name resolution.

ENVIRONMENT

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:

NAME

The name of the target directory entry.

When the ENVIRONMENT method returns 1, indicating that it handled the request, the information directory entry RESULT contains the directory entry. When RESULT is not set and the method returns 1, this is the same as a failure to find an entry in the .ENVIRONMENT directory. Rexx continues with the next step in the name resolution.

STREAM

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:

NAME

The name of the target stream.

When the STREAM method returns 1, the information directory STREAM must be set to an object to be used as the stream target. This should be a stream object or another object that supports the Stream class methods.

METHOD

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:

OBJECT

The object the protected method is issued against.

NAME

The name of the protected method.

ARGUMENTS

An array containing the method arguments.

When the METHOD method returns 1, indicating that it handled the external call, the function result can be placed in the information directory as the method RESULT.

Example

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):

  1. The audit manager (Dumper class) writes a record of the event but then permits the action.

  2. The closed cell manager (noWay class) does not permit the action to take place and raises an error.

  3. 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     */