Configuration of Constable To achieve better flexibility and a richer configuration, Constable interprets its configuration file as a kind of program. More precisely, it is a set of handlers (functions). Each handler corresponds to a certain type or subtype, depending on the actual data, of query from the kernel, and, depending on the data from the kernel, permits or forbids the operation. If there is more than one handler that match the request, all execute. If no handler for some type of query exists, then Constable returns an error, and the operation is executed normally, as it would if Medusa did not exist. The configuration language is very similar to standard C. There are only few things different and some features added _ some special keywords, etc.. A description of the configuration language follows: BASICS Programs consist of commands, which end with a semicolon, and of blocks of commands, which are marked with '{' and '}'. Any string between '/*' and '*/', or after '//' to the end of the line, is considered to be a comment. Strings have to be between two '"'. The format of integer values is 'VALUE' for decimal, '0bVALUE' for binary and '0xVALUE' for hexadecimal. These are all implemented exactly as in C. FUNCTION DEFINITION The definition of functions is different. Functions are more similar to macros than to functions in C. Each function definition begins with the keyword 'function' followed by the function name and the body of the function in a command block. Functions can return a value using the keyword 'return', which exits the function with the specified return value. Here is an example: function admin_sees { if (ecap ?& CAP_SYS_ADMIN) { vss = 0b11111111; return 0; } return 1; } VARIABLES All variables are of integer type with lengths of 16 or 32 bits. This also includes pointers. Pointers to strings are a special type of pointer. A variable's type is automatically determined on variable initialization. There are two groups of variables: predefined and local. There are several predefined variables that have special meanings, and their identifiers are explicitly specified. For example, 'answer'. Most predefined variables are related to current data being processed such as various entries in the current process' task_struct, actual inode information, etc.. They provide some additional information about the subject of an actual request from the kernel, allowing you to fine tune your system security model. All predefined variables except pid, inode_vs, luid, action, info1 and info2 can be changed (but at your own risk; there are no checks). These changes are then reported back to the kernel and executed. Local variables that have identifiers beginning with $ (for example '$foo') are static and do not need to be declared, just as in bash or perl. Description of predefined variables and their values pid process id of the current process uid user id of the current process euid effective user id of the current process suid saved user id of the current process fsuid filesystem user id of the current process gid group id of the current process egid effective group id of the current process sgid saved group id of the current process fsgid filesystem group id of the current process luid login user id of the current process (this one is set to current->uid only at first call of set{r,e}uid for each process) vs bitmap of virtual spaces whose member is current object vss bitmap of virtual spaces whose members can see current process vsr bitmap of virtual spaces from which current process can read vsw bitmap of virtual spaces to which current process can write (all of these are currently 32 bit) flags user defined flags of the current process procact process actions for the current process which need confirmation from Constable P_FORK process fork P_EXEC execute program P_SEXEC execute set uid program P_EXIT process exit P_SETUID setuid, setreuid, setresuid P_KILL send signal P_FSACT filesystem actions (see below) P_CAP process POSIX capabilities check P_PTRACE process tracing fsact filesystem actions for the current process which need confirmation from Constable FS_ACCESS inode access FS_CREATE create file FS_LINK create hard link FS_UNLINK unlink inode (delete) FS_SYMLINK create symbolic link FS_MKDIR create directory FS_RMDIR delete directory FS_MKNOD create special device file FS_RENAME rename file FS_TRUNCATE truncate file FS_PERMISSION file permission check FS_EXEC execute file ecap effective POSIX capabilities of the current process (current file in "for set") icap inherited POSIX capabilities of the current process (current file in "for set") pcap permitted POSIX capabilities of the current process for capabilities constants see /usr/include/linux/capability.h (current file in "for set") fcap pcap (for filesystem use only) acap icap (for filesystem use only) data additional data following standard Medusa packet (usually filenames etc.) answer answer code for the kernel ERR error, indicates security daemon does not know how to handle request, so everything will proceed without change as though Medusa did not exist. YES permit operation NO forbid operation SKIP forbid operation, but return success OK permit operation, but proceed with standard system permission check if any (so permission could be still denied by standard system security mechanisms) this is the default apply information about processes for which to apply actual operation A_CURRENT apply operation to current process (this is the default) A_PARENT apply operation to current and parent process A_FOR_PARENT apply operation to parent process and all its children recursively A_FOR_LOGIN apply operation to login process with current luid and all its children recursively action system call number info1 additional information on filesystem or process action info2 additional information on filesystem or process action trace1 first parameter of the traced syscall trace2 second parameter of the traced syscall trace3 third parameter of the traced syscall trace4 fourth parameter of the traced syscall trace5 fifth parameter of the traced syscall target_pid process id of the target process (used in operations which have some target process, for example kill) target_uid user id of the target process target_euid effective user id of the target process target_suid saved user id of the target process target_fsuid filesystem user id of the target process target_gid group id of the target process target_egid effective group id of the target process target_luid login user id of the target process (this one is set to current->uid only at first call of set{r,e}uid for each process) target_vs bitmap of virtual spaces whose member include the target object target_vss bitmap of virtual spaces whose members can see target process target_vsr bitmap of virtual spaces from which target process can read target_vsw bitmap of virtual spaces to which target process can write target_flags user defined flags of the target process for process flags constants see /usr/include/linux/sched.h target_procact process actions for the target process which need confirmation from Constable target_fsact filesystem actions for the target process which need confirmation from Constable target_ecap effective POSIX capabilities of the target process target_icap inherited POSIX capabilities of the target process target_pcap permitted POSIX capabilities of the target process inode_uid actual file owner user id (actual file stands for file's inode VFS entry) inode_gid actual file owner group id inode_mode actual file mode for file mode constants see /usr/include/linux/stat.h or /usr/include/sys/stat.h inode_vs bitmap of virtual subsystems to which actual file belongs (changes to this variable are not applied back to the kernel) inode_fsact file actions for the actual file which need confirmation from Constable constable_pid process id of the Constable OPERATORS Operators are very similar to those used in C, but there are some changes and new operators. The C equivalent (marked <==>) will follow the description of each operator. All operators except 'not' are binary and associate left to right. Description of operators == equal a == b <==> a == b != not equal a != b <==> a != b > greater a > b <==> a > b < less a > b <==> a < b >= greater or equal a >= b <==> a >= b <= less or equal a <= b <==> a <= b ?& intersection a ?& b <==> (a & b) != 0 ?! disjunction a ?! b <==> (a & b) == 0 ?= superset a ?= b <==> (a & b) == b + plus a + b <==> a + b - minus a - b <==> a - b & bitwise AND a & b <==> a & b | bitwise OR a | b <==> a | b ^ bitwise XOR a ^ b <==> a ^ b = assignment a = b <==> a = b += assign sum a += b <==> a += b -= assign difference a -= b <==> a -= b |= assign bitwise OR a |= b <==> a |= b /= assign bitwise complement a /= b <==> a &= ~b ~= assign bitwise XOR a ~= b <==> a ^= b >>= assign right shift a >>= b <==> a >>= b <<= assign left shift a <<= b <==> a <<= b and logical AND a and b <==> a && b or logical OR a or b <==> a || b not logical NOT not a <==> !a FURTHER KEYWORDS AND COMMANDS Description of basic commands: function begins the definition of a function. It is followed by function name and body (see above) two parameters: function return exits function with return value one parameter: return if begins a conditional statement. If the expression between '(' and ')' (these go after 'if') is non-zero (true), then the following command block is executed. else placed after the end of the 'if' command block and before command block which is executed, if the 'if' expression evaluates to zero (false). 'if' and 'else' are implemented exactly the same way as in C redirect redirects any type of access to a given file to another file one parameter: redirect (this does not work for directories yet) example: for exec "/bin/ping" { if (vs ?= 0b1010 ) redirect "/usr/local/bin/ping-pong"; /* :-) */ } trace_on enables tracing of a specified syscall for current process one parameter: trace_on trace_off disables tracing of a specified syscall for current process one parameter: trace_off lpeek reads data from an address in te virtual address space of the current process, storing the result in a given variable two parameters: lpeek
lpoke writes a given value to an address in the virtual address space of the current process two parameters: lpoke
log log messages through the kernel logging facility This command can have multiple parameters. These are variables or strings to be logged. example: on syscall { log "uid = " uid "gid = " gid "number = " action; } log_inode this logs information about inode no parameters log_proc similar to log but it automatically logs information about the current process log_vproc similar to log_proc but it logs more information about the current process on process action handler followed by the type of handler and the handler body two parameters: on example: on exec { if ( (uid == 111) and (action == 1)) { flags |= 0x8; ecap |= CAP_SYS_MODULE; } This will give any process with certain user defined flags that is successfully executed under uid 111 the effective capabilities of admin for filesystem handler followed by the type of handler and the handler body three parameters: for example: for unlink "/etc/profile" { if ( pid > 234 ) { ecap = 0; log_fs "someone tried to delete me"; procact = P_EXEC; fsact = FS_CREATE; vsw /= 0b1000000000000000; answer = NO; } } This will deny and log an attempt of any process with process id greater than 234 to delete /etc/profile. It will also lower the process' effective capabilities, disallow writing to virtual space 31 for this process and enable watching what files this process executes and creates. force force execution of code by the current process The dynamic loader loads the object file, which has to be in ELF format and cannot use any dynamically linked libraries, because the loader does not support dynamic linking. Code is executed by calling symbol "main". Only direct numeric parameters are allowed (no addresses - you probably will not be able to dereference them). one or more parameters: force "" [arg0] [arg1] ... You can learn more about code forcing and programming functional code for it in code_forcing.doc recursive apply recursively for all subdirectories example: recursive for set "/var/log" inode_vs = 0b0000000000000001; this will set virtual space 0 for all files and directories in /var/log this directive is used only for filesystem handlers recur same as above Variables info1 and info2 have different meanings for each type of handler. Their meanings will be described for all handler types. Descriptions of the filesystem handler types The filename parameter of a filesystem handler has to be a string, but it can contain something similar to extended regular expressions, so more files can be matched even without using keyword 'recursive'. File stands for the current file (inode) being processed. A few examples: "/etc/.*conf" "/bin/(bash|tcsh|ash|ksh|zsh)" "/dev/{t,p}ty{,1,2,3,4,5,6,??}" set receive inode information from the Constable to the kernel, (vs stands for inode_vs in this type of handler, linux implementation of Medusa allows you to set file capabilities here, too) access file access create file creation, info1 = dentry, info2 = mode link creation of hard link to the file, info1 = dentry unlink file deletion, info1 = dentry symlink creation of symbolic link to the file, info1 = dentry mkdir directory creation, info1 = dentry, info2 = mode rmdir directory deletion, info1 = dentry mknod special device file creation, info1 = dentry, info2 = device number rename file renaming, info1 = dentry truncate file truncation, info1 = dentry, info2 = length permission file access permission check, info1 = file access mask exec file execution, info1 = argv, info2 = envp, action = 0 when attempting to execute the file, 1 when exec was successful (yes, this handler is called twice during exec operation) Description of the process action handler types Everything is related to the current process unless stated otherwise. init special action executed only at Constable startup (current process is constable here) fork fork system call, info1 = clone flags exec execution of program, info1 = argv, info2 = envp sexec execution of set uid program, info1 = inode vs setuid setuid, setreuid and setresuid system calls, info1 = uid kill sending signals, kill system call, info1 = signal number ptrace performing ptrace operations on target process capable process capability check, info1 = capability mask syscall any traced syscall (see above: action, trace) MISCELLANEOUS Constable and its configuration language absolutely are not stupid-proof. You can get an error when reading the configuration file and some runtime errors, but that is all. If you want to do some really stupid things, you can do them, and face the consequences. A partial reason for this is that you can alter some data structures in kernel and there is not much we can do with this. And if it would prevent you from doing many stupid things, it would also prevent you from doing many clever things. But if you build your configuration file carefully, it should be all right. The best way to do this is to keep in mind that anything you enable, you will have to handle later. Have fun.