$Id: syscall_tracker_toplevel_design.html,v 1.2 2001/09/09 16:06:52 chooga Exp $
TOC
The system call tracking module works in the following manner:
During the initialization of the module, an init function for each component is invoked. Generally, no special action is done during module initialization.
Cleanup - when the tracking module is being unloaded, it calls each component's cleanup function. These functions release any allocated memory. Note that module cleanup is possible only if all rules were removed, and thus the module currently hijacks no system call.
The system call tracking module works in the following manner:
The tracking module is made up of a set of software components, which are described in this section.
This component is responsible for hijacking system calls, and releasing hijacked system calls.
A table of tracking functions is kept by this component. One tracking function is defined for each system call, and thus this table looks similar to the kernel's system calls table 'sys_call_table'. The 'tracker_sys_call_table' is initialized at compile time. A NULL entry in this table determines that the system call matching this entry is not supported by the module. This will allow adding code to support new system calls incrementally.
sysctl or ioctl calls will be used to configure the tracking module. Each such sysctl/ioctl invocation will carry a structure containing a filtering rule and an action. Part of this structure is the same for all system calls, and contains the system call number, process state filtering conditions and the action to be taken. The parameters filtering conditions are defined differently for each system call (since each system call accepts different parameters of different types). Parameter filtering will be done based on parameter position and parameter content. All comparison operators need to handle implicit type casts (as done in most type-less scripting languages). It is up the the user-mode application to make sure that a comparison operator matches the type of the parameter for the system call (i.e. that the user doesn't try to match a string parameter with a number, or a numeric parameter with a string).
Each rule passed by the user to the kernel will have a numeric rule identifier, so the user will be able to delete certain rules without the need to delete all filtering rules. This rule identifier could also be used to temporarily disable a rule, and later on re-enable it. Disabled rules are skipped during system call filtering.
The rules matching engine will handle a rule list, and try to match a syscall invocation to each rule in the list in succession. When a rule matches, the engine will immediately move to acting on that rule, and not try to match later rules in the list. Thus, rule ordering is important. This is similar to how access lists in various fire-walls are handled.
The rule matching will be done using an expression evaluation component. Each filter rule will be specified as an expression (and stored using a tree representation), and evaluation will be done in the same manner as is done by compilers or interpreted languages. A filter expression will contain constants (numbers, strings), variables (that will allow matching against system call parameters or process state parameters) and operators (comparison operators, bitwise operators, logical operators).
Special note should be given to handling variables. These variables will be assigned values before the rule matching, so their values could be substituted when evaluating filter expressions. Variables could be indexed (e.g. a VT_PARAMS variable array will represent the system call parameters, with an index to denote the location of the parameter) or non-indexed (e.g. a VT_PROC_PID variable will represent the process-ID of the process invoking the system call).
As stated above, system call parameters will be stored in the indexed VT_PARAMS variable of the rule matching engine, at the beginning of the system call invocation, and before the filter expressions are evaluated. For that, the tracking function for that system call will fetch the parameters supplied to the system call, and then set them in the relevant indexes of the VT_PARAMS variable.
The various process state variables will be handled just like the VT_PARAMS variable array is initialized in each system call. The tracker function will get the various values (uid, gid, pid, comm, etc.) from the 'current' task structure, and set these values in the VT_PROC_UID, VT_PROC_GID, VT_PROC_PID and similar variables. These variables will be used during the filter expression evaluation.
The rules matching engine will eventually return the ID of the rule that matched, and/or a pointer to an action structure, specifying what should be done now (i.e. log the syscall, fail it, or some other action). In order for logging to work, the tracker function should pass a format string to the action engine, as well as all the syscall parameters that should be invoked.
The tracking engine would then check the action structure to decide what to do, and call a function to perform the proper action.
The logging component accepts a syscall ID, a format string and a variable number of arguments. It uses variable argument handling (va_start and its friends) to go over the arguments, and print them out. Logging is done using printk() calls, and should be done in a single line.
This component's design is yet to be defined.