UNIX systems historically were not designed to provide extensive security and auditing features. It's notion of superuser who is never stopped from interfering with system's policies and internals is considered, even by it's most dedicated fans, too dangerous. Permission bits for files are often too generous, which is caused not only by lack of standard ACL support in many systems (FreeBSD 4.x included), but also by need for setuid-bit existence in UNIX. In the old days UNIX was used in friendly academic communities where security was never top priority. Now it's widespread commerce, critical data processing and network security uses make enhancements to traditional UNIX security model essential for it's success.
CerbNG is written for FreeBSD operating system. The base OS offers many security enhancements beyond the standard UNIX feature set. Mechanisms like: Jail, ACLs, MACs, securelevels, enhanced file attributes (flags) help making the system more secure but not immune to remote and local security breaches. FreeBSD developers are known for safe coding practices, careful testing of new system features and good design. But very often system administrators and security officers are faced with critical bugs which were not caught despite so many eyes watching. Sometimes checking is not enough. Restricting applications beyond their initial rights, given by UID and GID bits of user running them, is crucial for maintaining system integrity.
Sysadmins can never assert, that the only threat to their systems are so called script kiddies - non-thinking, always boasting, clueless users of readily available exploits; looking for root level access because it sounds cool. What about determined attackers who often spend weeks finding system and application bugs which can be used for privilege elevation? You can't assume they will go public with their findings or notify software vendors before exploiting or sharing found vulnerabilities. Many of them don't. How can we fight the enemy? How can we prevent exploiting bugs we are not aware of?
For sure, many of the conscious FreeBSD users are bothered by generosity of credentials in base installation. Programs like ping(8) or named(8) are setuid root for very basic operations (raw sockets and low port binding respectively). FreeBSD as for version 4, doesn't offer any mechanisms restricting processes running with root UID to performing single operation with elevated credentials.
All of the above issues motivated development of CerbNG.
CerbNG contains two building blocks. First one, is kernel loadable module, second is userland compiler which translates rules from human readable format to rules and variables used by kernel module. CerbNG is a kind of interface between userland and kernel space. Without CerbNG loaded, all system calls are immediately executed by kernel. After loading CerbNG, all system calls and their arguments are passed through a series of tests defined in configuration rules. Before and after system call execution, system administrator using CerbNG is allowed to perform numerous modifications to running process environment and syscall arguments. Set of changeable parameters contains all the *UID registers permitting privilege elevation of running process. Additional assumptions in design of CerbNG were flexibility and nearly complete transparency. CerbNG can be configured in ways which don't require any changes to program source code and filesystem permissions.
Next we present some examples which uncover some of CerbNG mechanisms for access control. Let's start with securing ping. Ping was chosen as first example for simplicity not it's insecurity.
if (syscall == SYS_execve) {
reg[0] = realpath(arg[0]);
if (reg[0] == "/sbin/ping") {
reg[0] = call();
if (reg[0] == 0 && euid == 0) {
setpeuid(ruid);
}
return reg[0];
}
}
if (pname == "ping" && pinode == 123 && pdev == 456) {
if (syscall == SYS_socket) {
if (socket_domain == AF_INET &&
socket_type == SOCK_RAW &&
socket_protocol == IPPROTO_ICMP) {
reg[0] = euid;
setpeuid(0);
reg[1] = call();
setpeuid(reg[0]);
return reg[1];
}
}
}
if (syscall == SYS_execve) {
Check if execve(2) was called.
reg[0] = realpath(arg[0]);
Internal register number zero is assigned realpath of program which user owning this syscall is attempting to run (first argument of execve(2)). In other words, register zero will store absolute path of the program which the user is trying to run.
if (reg[0] == "/sbin/ping") {
Check if the program being run is ping(8).
reg[0] = call();
Register zero is assigned return value as the syscall after it's execution (call()).
if (reg[0] == 0 && euid == 0) {
Check if system call succeeded (zero return value), and if effective UID is equal zero (it should be if ping is setuid root).
setpeuid(ruid);
Downgrade credentials of ping(8) to those of user running it.
return reg[0];
Terminate syscall execution returning value stored in register zero.
Purpose of the above fragment was restricting credentials of ping(8) without taking away it's setuid bit.
Next part is responsible for granting root credentials to ping only when it opens raw socket. In practice that's the only reason for ping being setuid root.
if (pname == "ping" && pinode == 123 && pdev == 456) {
Check if process name is ping, program inode equals 123 and it's device number equals 456. These values can be precomputed during compilation with functions GET_INODE and GET_DEV. Precise process identification is crucial. We have to make sure that the process being run is system ping, not any other program called as ping.
if (syscall == SYS_socket) {
Check if syscall started by ping is socket(2)
if (socket_domain == AF_INET &&
socket_type == SOCK_RAW &&
socket_protocol == IPPROTO_ICMP)
{
Set of comparisons of syscall arguments to check is type of opened socket is SOCK_RAW.
reg[0] = euid;
Store process effective UID in register zero.
setpeuid(0);
Set effective UID to zero, to make the syscall succeed.
reg[1] = call();
Run the syscall and store it's return value in register number 1.
setpeuid(reg[0]);
Restore saved credentials
return reg[1];
Return syscall return value.
That's it all for ping
Using techniques shown above we can restrict most critical setuid programs. But CerbNG can do much more. Here is another example..
if (syscall == SYS_execve) {
if (pruid >= 1000) {
rmld("LD_*");
reg[0] = realpath(arg[1]);
if (reg[0] @ "/usr/home/*" ||
reg[0] @ "/tmp/*" ||
reg[0] @ "/var/tmp/*") {
log(LOG_WARNING, "!WARN! Don't have permission to run "
"%s (proc=%s, ruid=%u, rgid=%u, euid=%u, egid=%u)",
reg[0], pname, ruid, rgid, euid, egid);
return EPERM;
}
}
}
The above rule set is equivalent to noexec option to mount. But it can prevent execution on directory basis additionally stopping LD_ tricks by removal of variables starting with "LD_" prefix. Only users with UID>= 1000 are checked which is more flexible than mount approach. Every unsuccessfully attempt of execve call is logged.
Another more secure approach for allowing execve is assigning all users allowed to run their programs to group "exec".
if (syscall == SYS_execve) {
if (ruid >= 1000) {
if (tabindex(GET_GID("exec")) < 0) {
rmenv("LD_*");
if (getouid(arg[1]) >= 1000) {
log(LOG_WARNING, "!WARN! Don't have permission "
"to run %s (proc=%s, ruid=%u, rgid=%u, "
"euid=%u, egid=%u)", realpath(arg[1]),
pname, ruid, rgid, euid, egid);
return EPERM;
}
}
}
}
We check if the real UID is not less than 1000, next check tests the membership of group "exec". Similarly to previous example, all LD_ variables are removed. If owner of the file being run with execve is not less than 1000 the the unsuccessful attempt is logged and syscall fails (EPERM is returned).
Not all sysadmins like the ability of their users to create hard links to files they don't own (hardlinked file cannot be removed until the link count goes to 0, which can be used to hold buggy setuid programs in the system without administrator's notice). Let's deal with this too:
if (syscall == SYS_link) {
if (ruid >= 1000) {
if (getouid(arg[0]) != ruid) {
log(LOG_WARNING, "!WARN! Don't have permission for link"
" creation to %s (proc=%s, ruid=%u, rgid=%u, "
"euid=%u, egid=%u)", realpath(arg[0]), pname, ruid,
rgid, euid, egid);
return EPERM;
}
}
}
If we care about users' privacy, it's sometimes important to restrict ability of reading the whole /etc/passwd and /etc/pwd.db by any user. It breaks long file listings (ls -l) and some other utilities which use password database for username mapping, but it can be resolved by finer restrictions' tuning. To give users access to only their entries in password databases, we can generate such a file and redirect open to those fake files like this:
if (syscall == SYS_open) { if (ruid >= 1000) { reg[0] = realpath(arg[0]); if (reg[0] == "/etc/passwd") { arg[0] = genstr("/etc/private/%u.passwd", ruid); } else { if (reg[0] == "/etc/pwd.db") { arg[0] = genstr("/etc/private/%u.pwd.db", ruid); } } log(LOG_INFO, "!INFO! Open of file %s was directed to %s " "(proc=%s, ruid=%u, rgid=%u, euid=%u, egid=%u)", reg[0], arg[0], pname, ruid, rgid, euid, egid); return call(); } }Now every user with UID not less than 1000 instead of /etc/passwd will open /etc/private/<login name>
Sysctl name | Description |
---|---|
cerb.rules.lock | 0/1 toggle, if set to 1 (enabled) new rules cannot be loaded to kernel. Once enabled cannot be disabled without rebooting the system (similar lock takes place when kern.securelevel sysctl is set to 1 or more) |
cerb.rules.show | setting this sysctl causes listing of rules loaded into specified slot or all rules if set to -1 |
cerb.rules.set | sets specified slot as active, or disables CerbNG if set to -1 |
cerb.syscalls.show | Displays syscall set associated with given rule set. If set to -1 displays syscalls intercepted by all rule sets |
cerb.threads.allocated | number of slots allocated for CerbNG threads |
cerb.rules.rm | removes all rules from given slot or all rules if set to -1 |
cerb.mem.stat | set to 1, displays all memory areas allocated by CerbNG |
cerb.mem.safe_malloc | if set to 0, memory is allocated without debugging information (faster), if set to 1, each memory chunk is allocated with comments to help finding memory leaks |
cerb.mem.debug | if set to 1, each byte allocated and freed by CerbNG will be set to 0xd0. this idea was taken from phkmalloc |
cerb.fdesc.stat | if set to 0, file name cache will be disabled and using them for fd2*() operations will be impossible. if set to 1, caching is performed using fdcache() only. if set to 2, each open file is cached, which comes with significant performance hit on I/O oriented machines. thus setting to 1 is recommended |
cerb.fdesc.show | displays file names matching given pattern |
cerb.fdesc.clear | flushes the descriptor cache |
cerb.ask.wakeup | resumes process suspended by ask function and returns passed value to kernel. it should be set to string formated as "<ID>:<retval>". for example setting this sysctl to "666:1" resumes process stopped with ID 666 and returns 1 to to cerb thread holding the process on ask function |
cerb.ask.show | returns list of suspended processes |
cerb.ask.suspended | returns number of suspended processes |
cerb.ask.maxsuspended | contains maximum number of procesess held on the ask hook. can be set to any number. |
cerb.misc.off_on_error | if set to 1, CerbNG is disabled when error in loaded rules is encountered, which might render the system unusable. should be set to 1 after rules pass your tests |
cerb.trace.kind | if set to 0, trace output will show only already executed rules. if set to 1, all rules will be shown |
cerb.trace.on_error | if set to 1, precise location of runtime error will be shown using trace mechanism. set to 0 causes display of the information on error encountered |
Operators | Operands | Binding |
---|---|---|
= | binary | right |
|| | binary | left |
&& | binary | left |
| | binary | left |
^ | binary | left |
& | binary | left |
== != | binary | left |
@ !@ (pattern matching) | binary | left |
< <= > >= | binary | left |
<< >> | binary | left |
+ - | binary | left |
* / % | binary | left |
! ~ - | unary | right |
All of the above operators work identically to C language operators
Function name | Description |
---|---|
ADD_SYSCALL(<sysc1>[,...]) | expands the set of intercepted syscalls |
GET_UID(<username>) | returns UID of user |
GET_UGID(<username>) | returns GID of user |
GET_GID(<username>) | returns GID of group |
GET_HOME(<username>) | returns home directory of user |
GET_SHELL(<username>) | returns login shell of user |
GET_OUID(<path>) | returns UID of file owner |
GET_OGID(<path>) | returns GID of file owner |
GET_INODE(<absolute path>) | returns inode number of file designated by path |
GET_DEV(<absolute path>) | returns device number for specified path |
GET_MODE(<absolute path>) | returns permission bits for specified file |
GET_FLAGS(<absolute path>) | returns FreeBSD specific filesystem flags for given path |
GET_NLINKS(<absolute path>) | returns number of hard links for specified file |
GET_SIZE(<path>) | returns size of file in bytes |
MKNULL(<type>) | returns NULL pointer of given type (equivalent of kernel mode null call) |
DEF:call() | |
description | Executes currently intercepted syscall |
return value | return values of executed syscall |
documentation | syscall(2) |
conferr | EINVAL - if redundant arguments were passed |
DEF:sucall([UDEF:UID]) | |
description | Executes currently intercepted syscall changing effective UID to specified UID (or UID 0 if UID is not specified) and changing the UID to saved value after syscall execution. |
return value | return value of executed syscall |
documentation | syscall(2), seteuid(2) |
conferr | EINVAL - incorrect argument number or type |
VOID:return(<DEF:errno>) | |
description | Immediate exit with passed errno value |
return values | none |
documentation | intro(2) |
conferr | EINVAL - incorrect argument number or type |
STR:realpath(<STR:path>) | |
description | Returns full absolute path of specified relative path (analogous to userland realpath(3) function) |
return values | absolute path starting with '/', with all symbolic links expanded, with '/' duplicates squashed, '/./'s removed and '/../' resolved. Empty string is returned on failure. |
documentation | realpath(1), realpath(3), readlink(2), getcwd(2) |
notice | the last element of path (file/directory name) has to exist for open's and mkdir's sake when new file/directory is created. |
conferr | EINVAL - incorrect argument number or type |
errno | EFAULT - passed path was NULL |
UDEF:setsyscall(<UDEF:number>) | |
description | Changes current syscall number. This function might be really useful for calling setuid instead of seteuid or such. Using this function several syscalls can be executed instead of one - current syscall, like this: if (syscall == SYS_setuid) { setsyscall(SYS_setuid); call(); setsyscall(SYS_setgid); return call(};It's a little strange example, but shows how to use the function. |
return values | syscall number before the change |
documentation | syscall(2) |
UDEF:exists(<STR:path|UDEF:PID>) | |
description | Checks for existence of path or process |
return values | 1 if given entity exists, 0 otherwise |
conferr | EINVAL - incorrect argument number or type |
errno | All returned by namei(9) |
STR:getpname(<UDEF:PID>) | |
description | Returns name of current process or name of process identified by given PID. |
return values | string containing name of the process or NULL if process doesn't exist |
documentation | execve(2) |
notice | for current use variable pname |
conferr | EINVAL - incorrect argument number or type |
errno | ESRCH - process with given PID unexists |
STR:setpname([UDEF:PID,]<STR:name>) | |
description | Sets name of current process (or process identified by PID)
to string |
return values | 1 for success, 0 for failure (process with given PID doesn't exist) |
conferr | EINVAL - incorrect argument number or type |
errno | ESRCH - process with given PID unexists |
UDEF:setpid([UDEF:PID,]<UDEF:newpid>) | |
description | Sets PID of current or given process to specified value |
return values | previous PID or -1 (type DEF) on error |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID unexists |
UDEF:getppid([PID]) | |
description | Returns parent PID for process with given PID |
return | PID number of parent process, or -1 if error occurs |
documentation | getppid(2) |
notice | Use variable PPID for current process parent PID |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:setppid([UDEF:PID,]<UDEF:newpid>) | |
description | Sets PID of current or given process to specified value |
return values | previous PID or -1 (type DEF) on error |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID unexists |
UDEF:getpgid(<UDEF:pid>) | |
description | Returns number of process group, given process belongs to |
return values | process group number or -1 on error |
documentation | getpgid(2) |
notice | for current process use pgid variable |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID unexists |
UDEF:setpgid([<UDEF:pid>,]<UDEF:newpgid>) | |
description | Changes process group for current or given process to newpgid |
return values | previous process group number or -1 (DEF) on error |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given pid unexists or there is no process group with newpgid |
UDEF:getpsid(<UDEF:pid>) | |
description | Returns session number for given process |
return values | process SID or -1 (DEF) on error |
notice | for current process use variable psid |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID unexists |
STR:getpfile(<UDEF:PID>) | |
description | Returns absolute path to executable of process with given PID |
return values | absolute path (string) or "" if process doesn't exist |
notice | for current process use variable fname |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL - EBADF - ENOTDIR - ENOENT - ENAMETOOLONG - |
STR:setpfile([UDEF:PID],<STR:path>) | |
description | Sets path to file executed by current process (or some other
process with given PID) to string |
return values | 1 success, 0 failure (returned only when given process doesn't exist) |
notice | in fact only p->p_textvp is changed |
documentation | vnode(9) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:getpruid(<UDEF:PID>) | |
description | Returns real UID for specified process |
return values | real UID or -1 for non existing process |
documentation | getuid(2) |
notice | for current process use variable ruid |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:setpruid([UDEF:PID],<UDEF:UID>) | |
description | Sets real UID for given (current) process to |
return values | 1 for success, 0 for failure (non existing process) |
documentation | setruid(3), setuid(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:getprgid(<UDEF:PID>) | |
description | Returns GID of given process |
return values | group id or -1 for non existing process |
documentation | getgid(2) |
notice | use variable rgid for current process |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:setprgid([UDEF:PID],<UDEF:GID>) | |
description | Sets real GID for given (current) process to |
return values | 1 for success, 0 for failure (non existing process) |
documentation | setrgid(3), setgid(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:getpeuid(<UDEF:PID>) | |
description | Returns effective UID of given process |
return values | real UID or -1 for non existing process |
documentation | geteuid(2) |
notice | use variable euid for current process |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:setpeuid([UDEF:PID],<UDEF:UID>) | |
description | Sets effective UID for given (current) process to |
return values | 1 for success, 0 for failure (non existing process) |
documentation | seteuid(3), setuid(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:getpegid(<UDEF:PID>) | |
description | Returns effective GID of given process |
return values | effective GID or -1 for non existing process |
documentation | getegid(2) |
notice | use egid for current process |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:setpegid([UDEF:PID],<UDEF:GID>) | |
description | Sets effective GID for given (current) process to |
return values | 1 for success, 0 for failure (non existing process) |
documentation | setegid(3), setgid(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:getpsvuid(<UDEF:PID>) | |
description | Returns saved UID of given process |
return values | saved UID or -1 for non existing process |
documentation | getresuid(2) |
notice | use variable svuid for current process |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:setpsvuid([UDEF:PID],<UDEF:UID>) | |
description | Sets saved UID for given (current) process to |
return values | 1 for success, 0 for failure (non existing process) |
documentation | setresuid(3), setuid(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:getpsvgid(<UDEF:PID>) | |
description | Returns saved GID of current (given) process |
return values | saved GID or -1 for non existing process |
documentation | getresgid(2) |
notice | use variable svgid for current process |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:setpsvgid([UDEF:PID],<UDEF:GID>) | |
description | Sets saved GID for given (current) process to |
return values | 1 for success, 0 for failure (non existing process) |
documentation | setresgid(3), setgid(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:getpumask(<UDEF:PID>) | |
description | Returns umask of given process |
return values | umask or -1 for non existing process |
documentation | umask(2) |
notice | use variable umask for current process |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:setpumask([UDEF:PID,]<UDEFumask>) | |
description | Sets umask for given (current) process to |
return values | new umask for success, 0 for failure (non existing process) |
documentation | umask(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEFPTR:getpgroups([UDEF:PID]) | |
description | Returns table containing group id's of given process |
return values | table containing group id's or NULL if given process doesn't exist |
documentation | getgroups(2) |
notice | use variable groups for current process |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
STR:getplogin(<UDEF:pid>) | |
description | Returns login name for session owner, set using setlogin(2) for given process |
return values | login name or NULL on error |
documentation | getlogin(2), setlogin(2) |
notice | for current process use login variable |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID unexists |
STR:setplogin(<UDEF:pid>) | |
description | Sets login name for session owner of process with given PID |
return values | Previous login name or NULL on error |
documentation | getlogin(2), setlogin(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID unexists |
UDEFPTR:setpgroups([UDEF:PID],<UDEFPTR:gidtab>) | |
description | Sets group list for current (given) process to |
return values | new GID table or NULL if process doesn't exist |
documentation | setgroups(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEFPTR:addgroup([UDEF:PID,]<UDEF:GID>) | |
description | Grants membership of given or current process to group with specified GID |
return values | array with previous groups or NULL if an error occurred |
documentation | setgroups(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEFPTR:delgroup([UDEF:PID,]<UDEF:GID>) | |
description | Removes membership of given or current process to group with specified GID |
return values | array with previous groups or NULL if an error occurred |
documentation | setgroups(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:tabindex(<(DEF|UDEF|STR):element>,<(DEF|UDEF|STR)PTR:tab>) | |
description | Checks membership of element in given table |
return values | number of element if it's in the table (> 0), -1 if it's not a table member |
conferr | EINVAL - incorrect argument number or type |
UDEF:matchmember(<(DEF|UDEF|STR):element>,<(DEF|UDEF|STR)PTR:tab>) | |
description | Checks if any of tab's elements matches the pattern |
return values | number of matching element or -1 if no element was found |
notice | if DEF/UDEF elements are checked, this operation is equivalent to tabindex() |
conferr | EINVAL - incorrect argument number or type |
example | matchmember("*ab*", ["def", "abc", "ghi"]) returns 1 |
UDEF:matchtab(<(DEF|UDEF|STR):element>, <(DEF|UDEF|STR)PTR:matchtab>) | |
description | Checks if element matches any patterns present in matchtab |
return values | number of patter in the matchtab matching elem, or -1 if not pattern was found |
notice | if DEF/UDEF are checked, this operation is equivalent to tabindex() |
conferr | EINVAL - incorrect argument number or type |
example | matchtab("abc", [ "*def*", "ab*", "*hi" ]) returns 1. |
(DEF|UDEF|STR)PTR:addelem(<(DEF|UDEF|STR)PTR:tab>,<(DEF|UDEF|STR):element> [,(DEF|UDEF):after]) |
|
description | Appends element to table tab, or places given element after element number after |
return values | returns pointer to created array or NULL if an error was encountered |
notice | to set an element at the beginning of table, put negative number in after argument |
conferr | EINVAL - incorrect argument number or type |
(DEF|UDEF|STR)PTR:rmelem(<(DEF|UDEF|STR)PTR:tab>,<(DEF|UDEF|STR):no>) | |
description | Removes from table tab element with number no |
return values | returns pointer to table with one element removed or NULL if an error occurred |
conferr | EINVAL - incorrect argument number or type |
error | EFAULT - table pointer is NULL EINVAL - the table doesn't contain specified element |
DEF:ask(<DEF:value>,<UDEF:timeout>,<STR:format>[,?:arg1[,?:arg2...]]) | |
description | Stops the process, sends the ID number of current event and sends to userland information containing the ID, text generated with format and arguments following it. The process will be resumed when an answer from userland comes. It has to contain the ID and a value, which will be later returned by ask. Ask can also return value if there was no answer from userland before `timeout' seconds elapsed or there are cerb.ask.maxsuspended processes already suspended |
return values | value passed from userland or the default `value' passed as first argument |
documentation | tsleep(9) |
notice | timeout is disabled when timeout argument is equal 0 |
procerr | EINTR - the suspended process received signal, or CerbNG is just being unloaded. This error is immediately returned to the userland and further rules are discarded |
conferr | EINVAL - incorrect argument number or type |
errno | EINVAL - there are too many suspended processes |
UDEF:getfamily(<ST_SOCKADDR:socket>) | |
description | Retrieves from argument of type sockaddr_in field sin_family |
return values | socket type or -1 (DEF) on error |
documentation | socket(2), ip(4) |
notice | this operation is useful ie for bind(2) syscall |
conferr | EINVAL - incorrect argument number or type |
errno | EINVAL - socket argument is NULL |
STR:getip(<ST_SOCKADDR:socket>) | |
description | Retrieves from argument of type sockaddr_in string representing the IP address |
return values | string containing the IP address or NULL on error |
documentation | socket(2), ip(4) |
notice | this operation is useful ie. for bind(2) syscall |
conferr | EINVAL - incorrect argument number or type |
errno | EFAULT - socket argument is NULL |
STR:getunpath(<ST_SOCKADDR:socket>) | |
description | Returns filesystem path from argument of type st_sockaddr |
return values | path or NULL on error |
notice | this operation is useful ie for bind(2) syscall |
conferr | EINVAL - incorrect argument number or type |
errno | EFAULT - socket argument is NULL |
UDEF:isjailed([UDEF:PID]) | |
description | Check if current (given) process is jailed |
return values | 1 if process is jailed, 0 process is not jailed, -1 an error occurred |
documentation | jail(8), jail(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
STR:getjailhost([UDEF:PID|ST_PRISON:jail]) | |
description | Returns hostname of jail which encloses current (given) process, or hostname of jail with given ST_PRISON. |
return values | string containing hostname of jail or empty string if process is not jailed |
documentation | jail(8), jail(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
STR:getjaildir([UDEF:PID]) | |
description | Returns path to root directory of jail enclosing current (given) process |
return values | absolute path or empty string if the process is not jailed |
documentation | jail(8), jail(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
STR:getjailip([UDEF:PID|ST_PRISON:jail]) | |
description | Returns IP address assigned to jail enclosing given process or IP address assigned to given jail. jail_st type is ST_PRISON. |
return values | string containing IP address of jail containing given process or empty string is process is `free' or doesn't exist |
documentation | jail(8), jail(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:getinode(<STR:path|UDEF:PID>) | |
description | Returns inode number of executable file running as given process or inode number of given path. |
return values | inode number or -1 if path or process is invalid (doesn't exist) |
documentation | stat(2) |
notice | for current process disk file use variable finode |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EFAULT - path is NULL |
UDEF:getdev(<STR:path|UDEF:PID>) | |
description | Similar to getinode, but returns device number of executable/path specified as argument |
return values | positive number or -1 if PID/path doesn't exist |
documentation | stat(2) |
notice | for current process use variable fdev |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist |
UDEF:getouid(<STR:path|UDEF:PID>) | |
description | Returns owner UID for given file or executable for process with given PID |
return values | UID number or -1 if path/executable doesn't exist |
documentation | stat(2), chown(2), chown(8) |
notice | for current process use variable fuid |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EFAULT - path is NULL + all error codes returned by namei(9) or vn_stat(?) |
UDEF:getogid(<STR:path|UDEF:PID>) | |
description | Returns owner GID for given file o executable for process with given PID |
return values | GID number or -1 if path/executable doesn't exist |
documentation | stat(2), chown(2), chown(8) |
notice | for current process use variable fgid |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EFAULT - path is NULL + all error codes returned by namei(9) or vn_stat(?) |
UDEF:getmode(<STR:path|UDEF:PID>) | |
description | Returns mode bits for executable file for specified process or given path |
return values | number representing access modes or -1 if given path or process doesn't exist |
documentation | stat(2), chmod(1), chmod(2) |
notice | for current process use variable fmode |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EFAULT - path is NULL + all error codes returned by namei(9) or vn_stat(?) |
UDEF:getflags(<STR:path|UDEF:PID>) | |
description | Reports FreeBSD specific flags for given path or executable of running process |
return values | either proper flags as number or -1 if process/path doesn't exist |
documentation | stat(2), chflags(1), chflags(2) |
notice | for current process use variable fflags |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EFAULT - path is NULL + all error codes returned by namei(9) or vn_stat(?) |
UDEF:getnlinks(<STR:path|UDEF:PID>) | |
description | Reports number of hard links to executable of running process or given path |
return values | number of hard links or -1 if specified process or directory doesn't exist |
documentation | stat(2), ln(1), link(2) |
notice | for current process use variable fnlinks |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EFAULT - path is NULL + all error codes returned by namei(9) or vn_stat(?) |
UDEF:getsize([STR:path|STR:PID]) | |
description | Returns size in bytes of executable of running process or specified file |
return values | size in bytes or -1 if process/path doesn't exist |
documentation | stat(2) |
notice | for current process use variable fsize |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EFAULT - path is NULL + all error codes returned by namei(9) or vn_stat(?) |
UDEF:fdcache(<DEF:fd>,<STR:path>) | |
description | Stores given path in file descriptor cache, which can be later retrieved by descriptor fd |
return values | 0 path has been put into cache, -1 an error occurred |
notice | Using this operation, retrieving filename associated with given descriptor is possible, using fd2name() function. if cerb.fdesc.stat is equal 2, this function is not necessary, since all descriptors are cached on open(2), but this is very inefficient. thus recommended caching policy is using this function when appropriate and setting cerb.fdesc.stat to 1 |
conferr | EINVAL - incorrect argument number or type |
error | EINVAL - incorrect descriptor number EFAULT - path is NULL or finding absolute path was impossible |
STR:fd2name([UDEF:PID,]<DEF:fd>) | |
description | Returns absolute path of file associated with given descriptor of current or given process |
return values | full path to file/directory or "" if operation cannot be completed |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL - incorrect descriptor number or descriptor is not associated with any open file (although it might be an open socket) |
UDEF:fd2inode([UDEF:PID,]<DEF:fd>) | |
description | Returns inode number of file associated with specified descriptor open by current or given process |
return values | inode number or -1 if descriptor isn't open or process doesn't exist |
documentation | stat(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL - incorrect descriptor number or descriptor is not associated with any open file (although it might be an open socket) + all error codes which vn_stat(?) can return |
UDEF:fd2dev([UDEF:PID,]<DEF:fd>) | |
description | Returns device number for file associated with given descriptor of current or specified process |
return values | proper device number or -1 if descriptor is closed or process unexists |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL - incorrect descriptor number or descriptor is not associated with any open file (although it might be an open socket) + all error codes which vn_stat(?) can return |
UDEF:fd2ouid([UDEF:PID,]<DEF:fd>) | |
description | Returns selected descriptor's associated file owner's UID. If PID is specified, the fd is taken from descriptor table of other process |
return values | proper UID or -1 if descriptor is closed or process doesn't exist |
documentation | stat(2), chown(2), chown(8) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL - incorrect descriptor number or descriptor is not associated with any open file (although it might be an open socket) + all error codes which vn_stat(?) can return |
UDEF:fd2ogid([UDEF:PID,]<DEF:fd>) | |
description | Returns selected descriptor's associated file owner's GID. If PID is specified, the fd is taken from descriptor table of other process |
return values | proper GID or -1 if descriptor is closed or process doesn't exist |
documentation | stat(2), chown(2), chown(8) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL - incorrect descriptor number or descriptor is not associated with any open file (although it might be an open socket) + all error codes which vn_stat(?) can return |
UDEF:fd2mode([UDEF:PID,]<DEF:fd>) | |
description | Returns selected descriptor's associated file permission bits. If PID is specified, the fd is taken from descriptor table of other process |
return values | proper mode or -1 if descriptor is closed or process doesn't exist |
documentation | stat(2), chmod(2), chmod(1) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL - incorrect descriptor number or descriptor is not associated with any open file (although it might be an open socket) + all error codes which vn_stat(?) can return |
UDEF:fd2type([UDEF:pid,]<DEF:fd>) | |
description | return type of file associated with given descriptor of given or current process |
return values | file type or -1 (DEF) on error |
documentation | stat(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL (??) - incorrect descriptor number or descriptor is not associated with open file (can be socket) + all error codes returned by vn_stat(?) |
UDEF:fd2flags([UDEF:PID,]<DEF:fd>) | |
description | Returns selected descriptor's associated file FreeBSD UFS flags. If PID is specified, the fd is taken from descriptor table of other process |
return values | proper flags as number or -1 if descriptor is closed or process doesn't exist |
documentation | stat(2), chflags(1), chflags(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL - incorrect descriptor number or descriptor is not associated with any open file (although it might be an open socket) + all error codes which vn_stat(?) can return |
UDEF:fd2nlinks([UDEF:PID,]<DEF:fd>) | |
description | Returns selected descriptor's associated file number of hard links. If PID is specified, the fd is taken from descriptor table of other process |
return values | number of hard links or -1 if descriptor is closed or process doesn't exist |
documentation | stat(2), ln(1), link(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL - incorrect descriptor number or descriptor is not associated with any open file (although it might be an open socket) + all error codes which vn_stat(?) can return |
UDEF:fd2size([UDEF:PID,]<DEF:fd>) | |
description | Returns selected descriptor's associated file size in bytes. If PID is specified, the fd is taken from descriptor table of other process |
return values | size in bytes or -1 if descriptor is closed or process doesn't exist |
documentation | stat(2) |
conferr | EINVAL - incorrect argument number or type |
error | ESRCH - process with given PID doesn't exist EINVAL - incorrect descriptor number or descriptor is not associated with any open file (although it might be an open socket) + all error codes which vn_stat(?) can return |
(DEF|UDEF|STR):sysctl(<(UDEFPTR|STR):name>[,(DEF|UDEF|STR):val]) | |
description | Either returns or sets value of sysctl with given name (for setting the value, val must be specified) |
return values | sysctl number if the value is checked, old value if the sysctl is set or -1 if an error occurs |
documentation | sysctl(3), sysctl(8) |
notice | This operation can modify only sysctls of types:
name argument can be either string or od table, which can be useful when we want to control sysctl(3) syscall. One should be careful using this operation, because in general you can't be certain value of which type will be returned. also you shouldn't consider -1 to be error indicator, since sysctl's of type CTLTYPE_INT and CTLTYPE_LONG can return -1. an error can occur, before CerbNG is able to determine type of sysctl used, which can make it return -1 when string value is expected (if there is an error determining sysctl type, always -1 of type DEF is returned). regarding all of the above, best way of dealing with this is testing errno variable after each call of this function, like in this example: reg[0] = sysctl("name.of.sysctl", <value>); if (errno != 0) { |
conferr | EINVAL - incorrect argument number or type |
error | EFAULT = the name argument is NULL or you tried to assign
NULL to sysctl of type CTLTYPE_STRING EINVAL - there was an overflow error, which means you cannot assign large UDEF value to sysctl of type CTLTYPE_INT, or a negative value to sysctl of type CTLTYPE_UINT, CTLTYPE_ULONG. +all error codes returned by kernel_sysctl() and sysctl_find_oid() |
STR:sysctlname(<UDEFPTR:valtab>) | |
description | Returns name of sysctl represented by oid table valtab |
return values | name of sysctl or NULL on error condition |
documentation | sysctl(3), sysctl(8) |
conferr | EINVAL - incorrect argument number or type |
error | EFAULT - valtab is NULL or it's size is zero EINVAL - number of sysctl components is not matching valtab size |
UDEF:isnull(<PTR:pointer>) | |
description | Checks if pointer is NULL |
return values | 1 - the pointer is NULL 0 - pointer is not NULL -1 - an error occurred |
conferr | EINVAL - incorrect argument number or type |
PTR:null(<UDEF:type>) | |
description | Returns NULL value of specified type (XXX) |
return values | NULL |
conferr | incorrect argument number or type |
UDEF:rmenv(<STR:match>) | |
description | Removes environment variables matching |
return values | number of variables removed or -1 if current syscall is not execve(2) |
documentation | environ(7), getenv(3) |
notice | this operation can be performed only when execve(2) is currently executed |
conferr | EINVAL - incorrect argument number or type |
errno | EINVAL - current syscall is not execve(2) EFAULT - address of array containing environment variables is illegal or the match argument is NULL |
(DEF|UDEF|STR)getelem(<(DEF|UDEF|STR)PTR:tab>,<UDEF:number>) | |
description | Returns element with given number from specified table of strings and/or numbers |
return values | proper element or -1/NULL on error |
notice | NULL returned when type of the table is STRPTR doesn't have to indicate an error. same for -1 and table type DEFPTR. good way to check for error in this case is checking errno value |
conferr | EINVAL - incorrect argument number or type |
errno | EINVAL - the number argument exceeds number of elements in
the table EFAULT - table address is NULL |
UDEF:gettabsize(<(DEF|UDEF|STR)PTR:tab>) | |
description | Returns number of elements in specified table |
return value | number of elements |
notice | if tab argument is NULL, 0 is returned and errno set to EFAULT |
conferr | EINVAL - incorrect argument number or type |
errno | EFAULT - array address is NULL |
(DEF|UDEF|STR)PTR:tabrange(<(DEF|UDEF|STR)PTR:tab>[,UDEF:start],<UDEF:size>) | |
description | Returns new table containing <size> |
return value | pointer to new table or NULL if an error occurred |
notice | this operation is also used for
determining table size |
conferr | EINVAL - incorrect argument number or type |
errno | EINVAL - start+size is larger than size of tab EFAULT - table is NULL |
STR:genstr(<STR:format>[,?:arg1][,...]) | |
description | Function formats it's arguments (<arg1>, ...) according
to format argument, which can contain subset of format specifies found in printf(3). Allowed special sequences: %d, %u, %o, %x, %s, %%. \ escapes next character. for full description see manual page for printf(3). additional Cerb-specific characters for format are: %D - array of signed numbers, %U - array of unsigned numbers, %X - array of numbers displayed in hexadecimal encoding, %S - array of strings, %? - argument of any type - CerbNG detects the type by itself, %A - arguments of currently intercepted syscall |
return value | generated string or NULL if an error occurred |
documentation | printf(3) |
notice | maximum length of generated string is defined in kcerb/cerb_globals.h as CB_GENSTRSIZE_G. If number of characters in generated string exceeds this value, the resulting string is truncated |
conferr | EINVAL - incorrect argument number or type |
error | EFAULT - format argument is NULL |
STR:log(<DEF:loglevel>,<STR:format>[,?:arg1][,...]) | |
description | Logs selected information. |
return value | logged string or NULL if an error occurred |
documentation | syslog(3), printf(3) |
notice | maximum length of logged message is defined in kcerb/cerb_globals.h as CB_LOGBUFSIZE_G. If number of characters in the message exceeds this value, the resulting string is truncated |
conferr | EINVAL - incorrect argument number or type |
errno | EFAULT - format argument is NULL |
UDEF:trace() | |
description | Displays trace information based on executed rules. |
return values | always zero. |
notice | trace has to be compiled into the module, ie. in cerb_globals.h you have to #define CERB_TRACE. on CONFERR errors, trace is always displayed |
conferr | EINVAL - spurious arguments were passed to trace() |
UDEF:type(<?:arg>) | |
description | returns argument type, all types are declared in cerb_types.h |
return values | argument type |
conferr | EINVAL - incorrect argument number |
UDEF:size(<?:arg) | |
description | Returns the size of argument in memory |
return values | Depending on type of passed argument, return values are: CB_DEF_T, CB_UDEF_T - always 0 CB_DEFPTR_T, CB_UDEFPTR_T, CB_STRPTR_T - number of elements in corresponding array CB_STR_T - length of the string |
conferr | EINVAL - incorrect argument number or type |
STR:basename(<STR:path>) | |
description | Return the last path component additionally removing trailing slashes |
return values | generated string |
documentation | basename(1), basename(3) |
notice | works just like basename(3) |
conferr | EINVAL - incorrect argument number or type |
STR:dirname(<STR:path>) | |
description | Returns path without it's last component (opposite to basename) |
return values | generated string |
documentation | dirname(1), dirname(3) |
notice | works just like dirname(3) |
conferr | EINVAL - incorrect argument number or type |