Info: Version 1.8.x is available.
Last modified: $Date: 2024-03-30 11:25:00 +0000 (Sat, 30 Mar 2024) $
3.2 Policy File's Modification
4.3 Access Logs
6.1 Allowing policy modification by non root user.
6.4 Sleep penalty for policy violation.
6.5 Judging execute request outside the kernel.
6.6 Invoking alternative program for execute requests that are not permitted by policy.
Used by /proc/ccs/profile and /etc/ccs/profile.conf
Used by /proc/ccs/exception_policy and /etc/ccs/exception_policy.conf
Used by /proc/ccs/domain_policy and /etc/ccs/domain_policy.conf
Used by /proc/ccs/manager and /etc/ccs/manager.conf
TOMOYO Linux performs pathname based access control. A pathname may contain not only alphabet and number but also space and carriage return and multibyte (e.g. kanji) characters. Thus, to be able to handle any characters correctly, TOMOYO Linux follows the rules shown below to represent a word. A word means all tokens that are treated as string data, such as pathnames, comments, environment variable's names, parameters for program execution.
| 0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | 0xA | 0xB | 0xC | 0xD | 0xE | 0xF | ||||
0x0 | \001 | \002 | \003 | \004 | \005 | \006 | \007 | \010 | \011 | \012 | \013 | \014 | \015 | \016 | \017 | |||||
0x1 | \020 | \021 | \022 | \023 | \024 | \025 | \026 | \027 | \030 | \031 | \032 | \033 | \034 | \035 | \036 | \037 | ||||
0x2 | \040 | ! | " | # | $ | % | & | ' | ( | ) | * | + | , | - | . | / | ||||
0x3 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | : | ; | < | = | > | ? | ||||
0x4 | @ | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | ||||
0x5 | P | Q | R | S | T | U | V | W | X | Y | Z | [ | \\ | ] | ^ | _ | ||||
0x6 | ` | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | ||||
0x7 | p | q | r | s | t | u | v | w | x | y | z | { | | | } | ~ | \177 | ||||
0x8 | \200 | \201 | \202 | \203 | \204 | \205 | \206 | \207 | \210 | \211 | \212 | \213 | \214 | \215 | \216 | \217 | ||||
0x9 | \220 | \221 | \222 | \223 | \224 | \225 | \226 | \227 | \230 | \231 | \232 | \233 | \234 | \235 | \236 | \237 | ||||
0xA | \240 | \241 | \242 | \243 | \244 | \245 | \246 | \247 | \250 | \251 | \252 | \253 | \254 | \255 | \256 | \257 | ||||
0xB | \260 | \261 | \262 | \263 | \264 | \265 | \266 | \267 | \270 | \271 | \272 | \273 | \274 | \275 | \276 | \277 | ||||
0xC | \300 | \301 | \302 | \303 | \304 | \305 | \306 | \307 | \310 | \311 | \312 | \313 | \314 | \315 | \316 | \317 | ||||
0xD | \320 | \321 | \322 | \323 | \324 | \325 | \326 | \327 | \330 | \331 | \332 | \333 | \334 | \335 | \336 | \337 | ||||
0xE | \340 | \341 | \342 | \343 | \344 | \345 | \346 | \347 | \350 | \351 | \352 | \353 | \354 | \355 | \356 | \357 | ||||
0xF | \360 | \361 | \362 | \363 | \364 | \365 | \366 | \367 | \370 | \371 | \372 | \373 | \374 | \375 | \376 | \377 |
Some examples are shown below.
Word | Correct expression | Wrong expression |
Hello world! | Hello\040world! | "Hello world!" |
/home/user/Documents and Settings/ | /home/user/Documents\040and\040Settings/ | /home/user/Documents and Settings/ |
Pathname must start with / character. Pathnames that end with / character are interpreted as directories, and that don't end with / character are interpreted as non-directories.
Pathname | Interpretation |
/ | A directory |
/tmp/ | A directory |
/tmp | Not a directory |
tmp/ | Invalid pathname |
Exceptions are pipes and sockets. Pipes begin with "pipe:" and sockets begin with "socket:" when these pathnames are accessed via /proc/PID/fd/ directory.
Like temporary files, pathnames may contain randomly selected characters. Thus, you often need to define pathnames using wildcards. TOMOYO Linux supports wildcards shown below.
Wildcard | Meaning | Example |
\* | Zero or more repetitions of characters other than '/'. | /var/log/samba/\* |
\@ | Zero or more repetitions of characters other than '/' or '.'. | /var/www/html/\@.html |
\? | 1 byte character other than '/'. | /tmp/mail.\?\?\?\?\?\? |
\$ | One or more repetitions of decimal digits. | /proc/\$/cmdline |
\+ | 1 decimal digit. | /var/tmp/my_work.\+ |
\X | One or more repetitions of hexadecimal digits. | /var/tmp/my-work.\X |
\x | 1 hexadecimal digit. | /tmp/my-work.\x |
\A | One or more repetitions of alphabet characters. | /var/log/my-work/\$-\A-\$.log |
\a | 1 alphabet character. | /home/users/\a/\*/public_html/\*.html |
\- | Pathname subtraction operator. |
|
/\{dir\}/ | Recursive directory matching operator which matches '/' + one or more repetitions of 'dir/'. (Valid only in 1.7.1 and later.) |
|
The memory used by TOMOYO Linux can be obtained via /proc/ccs/meminfo . The unit is byte.
# cat /proc/ccs/meminfo Policy: 377376 Audit logs: 0 Query lists: 0 Total: 377376 |
TOMOYO Linux supports memory quota for limiting maximum memory used by TOMOYO Linux.
You can set memory quota by writing to /etc/ccs/meminfo.conf .
# cat /etc/ccs/meminfo.conf Policy: 16777216 Audit logs: 1048576 Query lists: 1048576 |
Policy files are files that contain access permissions. These files are automatically loaded into the kernel upon boot.
When a system boots, /sbin/init is executed. When the execution of /sbin/init is requested and if /sbin/ccs-init exists, /sbin/ccs-init is executed, and /sbin/init is executed after /sbin/ccs-init terminates.
/sbin/ccs-init loads policy files in /etc/ccs/ directory via the kernel's /proc/ccs/ interface.
The kernel's interface | Policy file | Contents |
/proc/ccs/profile | /etc/ccs/profile.conf | Profiles (Collection of access control levels) |
/proc/ccs/manager | /etc/ccs/manager.conf | Managers (Programs that can modify policy via /proc/ccs/ interface) |
/proc/ccs/exception_policy | /etc/ccs/exception_policy.conf | Exception policy (Collection of exceptions for domain policy) |
/proc/ccs/domain_policy | /etc/ccs/domain_policy.conf | Domain policy (Access permissions given to individual domains) |
/proc/ccs/meminfo | /etc/ccs/meminfo.conf | Memory usage and quota. |
There are more interfaces for obtaining information. These interfaces don't have corresponding policy files.
The kernel's interface | Meaning |
/proc/ccs/query | Access requests that are waiting for administrator's decision. |
/proc/ccs/.domain_status | The list of domainnames and profile numbers currently defined in domain policy. |
/proc/ccs/grant_log | Access requests that didn't violate domain policy. |
/proc/ccs/reject_log | Access requests that violated domain policy. |
/proc/ccs/self_domain | The name of domain the current process belongs to. |
/proc/ccs/.process_status | The list of domainnames and profile numbers currently running processes belongs to. |
/proc/ccs/version | Version of TOMOYO Linux. |
Register the name of programs or domains that can modify policy via the kernel's /proc/ccs/ interface. Only
can modify policy via the kernel's /proc/ccs/ interface. Some examples are show below.
# cat /proc/ccs/manager /usr/sbin/ccs-loadpolicy /usr/sbin/ccs-editpolicy /usr/sbin/ccs-setlevel /usr/sbin/ccs-setprofile /usr/sbin/ccs-ld-watch /usr/sbin/ccs-queryd <kernel> /sbin/mingetty /bin/login /bin/bash |
By default, only processes with UID = 0 and EUID = 0 can modify policy via this interface. But by doing configurations described in Allowing policy modification by non root user., non root user can modify policy via this interface.
Exception is, processes that belong to domains with profiles for learning mode can append access permissions to /proc/ccs/domain_policy by simply requesting the access.
TOMOYO Linux gives access permissions as per a domain. It is managed via /proc/ccs/domain_policy.
In TOMOYO Linux, every process belongs to a single domain, and all programs belong to different domain. Even the two processes are executing the same program, if their previous domains differ, they belong to different domain.
All domains are defined originating from "<kernel>" domain, which the kernel process belongs to. Since /sbin/init is invoked by the "<kernel>" domain, the domain for /sbin/init is defined as "<kernel> /sbin/init". Since /etc/rc.d/rc is invoked by /sbin/init invoked by the kernel, the domain for /etc/rc.d/rc is defined as "<kernel> /sbin/init /etc/rc.d/rc".
When a process tries to execute a program, the steps shown below are performed.
Step | Procedure |
Getting program's name |
Get the name of program that the process is going to execute and keep it as "Candidate". This procedure does not solve symbolic link if the program is a symbolic link. |
Aggregating similar programs |
Search exception policy for
and if found one, replace "Candidate" with "aggregated name". |
Checking permission |
Search domain policy for
and deny the execute request if not found one. |
Deciding destination domain |
(1) Search exception policy for
and if found one, jump to (3). (2) Search exception policy for
and if found one, concatenate "the name of the domain that the kernel belongs to (i.e. <kernel>)" and "Candidate" and keep the result as destination domain, then jump to (6). (3) Search exception policy for
and if found one, jump to (5). (4) Search exception policy for
and if found one, set "the name of the domain the current process belongs to" as destination domain, then jump to (6). (5) Concatenate "the name of the domain the current process belongs to" and "Candidate" and keep the result as destination domain. (6) Check whether the destination domain is defined, and deny the execute request if not. |
Checking environment variable names |
(1) Examine all environment variables' names are granted in the destination domain, and deny the execute request if more than one of them are not granted. (2) Perform regular steps for executing program. If successfully completed, the process transits to destination domain. |
There is an exception. If either
and
the steps shown below are performed instead for the steps shown above. The usage of this exception is explained in "Judging execute request outside the kernel." and "Invoking alternative program for execute requests that are not permitted by policy."
Step | Procedure |
Getting program's name |
Keep the pathname of the program specified by denied_execute_handler or execute_handler and keep it as "Candidate". |
Appending information |
Append all environment variables to the tail of arguments, and delete all environment variables. Insert "Candidate" "the domainname the process that issued execute request belongs to" "the pathname of the process that issued execute request" "state of the process that issued execute request" "the pathname of the requested program" "number of arguments" "number of environment variables" to the top of arguments. |
Deciding destination domain |
(1) Search exception policy for
and if found one, jump to (3). (2) Search exception policy for
and if found one, concatenate "the name of the domain that the kernel belongs to (i.e. <kernel>)" and "Candidate" and keep the result as destination domain, then jump to (6). (3) Search exception policy for
and if found one, jump to (5). (4) Search exception policy for
and if found one, set "the name of the domain the current process belongs to" as destination domain, then jump to (6). (5) Concatenate "the name of the domain the current process belongs to" and "Candidate" and keep the result as destination domain. (6) Check whether the destination domain is defined, and deny the execute request if not. |
Execute program |
Perform regular steps for executing program. If successfully completed, the process transits to destination domain. |
TOMOYO Linux generates two types of access logs. One contains access requests that didn't violate domain policy. The other contains access requests that violated domain policy. The former is called grant log and is readable via /proc/ccs/grant_log . The latter is called reject log and is readable via /proc/ccs/reject_log . A utility program /usr/sbin/ccs-auditd is included for reading these logs and saving the logs as files.
Some examples are shown below. The first log is generated by execute request.
#2010-01-13 21:00:50# profile=1 mode=learning (global-pid=2908) task={ pid=2908 ppid=2879 uid=0 gid=0 euid=0 egid=0 suid=0 sgid=0 fsuid=0 fsgid=0 state[0]=0 state[1]=0 state[2]=0 type!=execute_handler } path1={ uid=0 gid=0 ino=852049 major=8 minor=1 perm=0755 type=file } path1.parent={ uid=0 gid=0 ino=851969 perm=0755 } exec={ realpath="/bin/cat" argc=2 envc=20 argv[]={ "cat" "/etc/fstab" } envp[]={ "HOSTNAME=tomoyo" "TERM=vt100" "SHELL=/bin/bash" "HISTSIZE=1000" "SSH_CLIENT=192.168.1.2\0402845\04022" "SSH_TTY=/dev/pts/0" "USER=root" "LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.sh=01;32:*.csh=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.cpio=01;31:*.jpg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35:" "MAIL=/var/spool/mail/root" "PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/sbin:/root/bin" "PWD=/root" "LANG=C" "SHLVL=1" "HOME=/root" "LOGNAME=root" "CVS_RSH=ssh" "SSH_CONNECTION=192.168.1.2\0402845\040192.168.1.7\04022" "LESSOPEN=|/usr/bin/lesspipe.sh\040%s" "G_BROKEN_FILENAMES=1" "_=/bin/cat" } } <kernel> /usr/sbin/sshd /bin/bash allow_execute /bin/cat |
This log shows that a process that belongs to "<kernel> /usr/sbin/sshd /bin/bash" domain attempted to execute /bin/cat , and the arguments were "cat" and "/etc/fstab", environment variables were "HOSTNAME=tomoyo" "TERM=vt100" "SHELL=/bin/bash" "HISTSIZE=1000" "SSH_CLIENT=192.168.1.2\0402845\04022" "SSH_TTY=/dev/pts/0" "USER=root" "LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.sh=01;32:*.csh=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.cpio=01;31:*.jpg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35:" "MAIL=/var/spool/mail/root" "PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/sbin:/root/bin" "PWD=/root" "LANG=C" "SHLVL=1" "HOME=/root" "LOGNAME=root" "CVS_RSH=ssh" "SSH_CONNECTION=192.168.1.2\0402845\040192.168.1.7\04022" "LESSOPEN=|/usr/bin/lesspipe.sh\040%s" "G_BROKEN_FILENAMES=1" "_=/bin/cat". Also, process information such as PID, UID are shown.
The next log is generated by opening a file for reading.
#2010-01-13 21:00:50# profile=1 mode=learning (global-pid=2908) task={ pid=2908 ppid=2879 uid=0 gid=0 euid=0 egid=0 suid=0 sgid=0 fsuid=0 fsgid=0 state[0]=0 state[1]=0 state[2]=0 type!=execute_handler } path1={ uid=0 gid=0 ino=901920 major=8 minor=1 perm=0644 type=file } path1.parent={ uid=0 gid=0 ino=901121 perm=0755 } <kernel> /usr/sbin/sshd /bin/bash /bin/cat allow_read /etc/fstab |
This log shows that a process that belongs to "<kernel> /usr/sbin/sshd /bin/bash /bin/cat" domain opened /etc/fstab for reading.
The next log is generated when a new domain is created.
#2010-01-13 21:05:22# profile=1 mode=learning (global-pid=3007) task={ pid=3007 ppid=2991 uid=0 gid=0 euid=0 egid=0 suid=0 sgid=0 fsuid=0 fsgid=0 state[0]=0 state[1]=0 state[2]=0 type=execute_handler } <kernel> /usr/sbin/sshd /bin/bash /bin/bash /bin/audit-exec-param /bin/cat use_profile 1 |
This log shows that a domain named "<kernel> /usr/sbin/sshd /bin/bash /bin/bash /bin/audit-exec-param /bin/cat" was created and profile 1 was assigned. TOMOYO Linux automatically creates domains as needed. When a domain is automatically created, the profile number of the domain the process that requested program execution belongs to is inherited.
The next log is generated when a program that is different from the program being requested was executed because of Judging execute request outside the kernel.
#2010-01-13 21:05:22# profile=1 mode=learning (global-pid=3007) task={ pid=3007 ppid=2991 uid=0 gid=0 euid=0 egid=0 suid=0 sgid=0 fsuid=0 fsgid=0 state[0]=0 state[1]=0 state[2]=0 type!=execute_handler } path1={ uid=0 gid=0 ino=360482 major=8 minor=1 perm=0755 type=file } path1.parent={ uid=0 gid=0 ino=851969 perm=0755 } exec={ realpath="/bin/audit-exec-param" argc=29 envc=0 argv[]={ "/bin/audit-exec-param" "<kernel>\040/usr/sbin/sshd\040/bin/bash\040/bin/bash" "/bin/bash" "pid=3007\040uid=0\040gid=0\040euid=0\040egid=0\040suid=0\040sgid=0\040fsuid=0\040fsgid=0\040state[0]=0\040state[1]=0\040state[2]=0" "/bin/cat" "2" "20" "cat" "/etc/fstab" "HOSTNAME=tomoyo" "SHELL=/bin/bash" "TERM=vt100" "HISTSIZE=1000" "SSH_CLIENT=192.168.1.2\0402845\04022" "SSH_TTY=/dev/pts/0" "USER=root" "LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.sh=01;32:*.csh=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.cpio=01;31:*.jpg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35:" "PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/sbin:/root/bin:/usr/sbin" "MAIL=/var/spool/mail/root" "PWD=/root" "LANG=C" "HOME=/root" "SHLVL=2" "LOGNAME=root" "CVS_RSH=ssh" "SSH_CONNECTION=192.168.1.2\0402845\040192.168.1.7\04022" "LESSOPEN=|/usr/bin/lesspipe.sh\040%s" "G_BROKEN_FILENAMES=1" "_=/bin/cat" } envp[]={ } } <kernel> /usr/sbin/sshd /bin/bash /bin/bash execute_handler /bin/audit-exec-param |
This log shows that a process that belongs to a domain named "<kernel> /usr/sbin/sshd /bin/bash /bin/bash" attempted to execute a program, but since the execute_handler keyword is specified to the domain, /bin/audit-exec-param was executed, and arguments passed to /bin/audit-exec-param were "/bin/audit-exec-param" "<kernel>\040/usr/sbin/sshd\040/bin/bash\040/bin/bash" "/bin/bash" "pid=3007\040uid=0\040gid=0\040euid=0\040egid=0\040suid=0\040sgid=0\040fsuid=0\040fsgid=0\040state[0]=0\040state[1]=0\040state[2]=0" "/bin/cat" "2" "20" "cat" "/etc/fstab" "HOSTNAME=tomoyo" "SHELL=/bin/bash" "TERM=vt100" "HISTSIZE=1000" "SSH_CLIENT=192.168.1.2\0402845\04022" "SSH_TTY=/dev/pts/0" "USER=root" "LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.sh=01;32:*.csh=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.cpio=01;31:*.jpg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35:" "PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/sbin:/root/bin:/usr/sbin" "MAIL=/var/spool/mail/root" "PWD=/root" "LANG=C" "HOME=/root" "SHLVL=2" "LOGNAME=root" "CVS_RSH=ssh" "SSH_CONNECTION=192.168.1.2\0402845\040192.168.1.7\04022" "LESSOPEN=|/usr/bin/lesspipe.sh\040%s" "G_BROKEN_FILENAMES=1" "_=/bin/cat". To avoid /bin/audit-exec-param affected by environment variables such as LD_PRELOAD, environment variables are moved to arguments.
In this way, an access log consists of 3 lines (or 4 lines since /usr/sbin/ccs-auditd inserts an empty line), and they are in the domain policy format and appendable to the domain policy. Pick up portions you want to permit from reject log and save (for example, /var/log/tomoyo/diff.txt) and you can add to domain policy by doing
# /usr/sbin/ccs-loadpolicy -d < /var/log/tomoyo/diff.txt |
Therefore, you don't need to use "learning mode" from the beginning. If you wish, you can use "permissive mode" from the beginning and let reject logs generated, then edit reject logs and append to domain policy when developing domain policy. When domain policy is generated by "learning mode", process state (the first line of an access logs) is not taken into account. But when domain policy is generated from reject logs, you can use Using conditional ACL. from the beginning. For example, generate a reject log by not using "learning mode" and append like
<kernel> /usr/sbin/sshd /bin/bash allow_execute /bin/cat if exec.argc=2 exec.realpath="/bin/cat" exec.argv[0]="cat" exec.argv[1]="/etc/fstab" |
then, you can give more precise permission compared to the permission appended by "learning mode"'s log (shown below).
<kernel> /usr/sbin/sshd /bin/bash allow_execute /bin/cat if exec.realpath="/bin/cat" exec.argv[0]="cat" |
TOMOYO Linux can perform several MACs besides MAC for files, but to reduce the load of policy managements, you can disable MACs you think unnecessary.
List up functions and their modes in "$number-$variable=$value" format. The $number is profile number between 0 and 255. To modify profile, use "ccs-setlevel" or "ccs-loadpolicy" commands.
Each domain is assigned one profile. To assign profile to domains, use "ccs-setprofile" or "ccs-editpolicy" or "ccs-loadpolicy" commands.
You can see profiles currently assigned to domains using "ccs-editpolicy" command.
You can see profiles currently assigned to processes using "ccs-pstree" command.
If you saved current policy using "ccs-savepolicy" command, the currently assigned profile number is saved as use_profile line of domain policy.
To read or modify current profiles, operate like below.
(Example)
cat /proc/ccs/profile
ccs-savepolicy -p
ccs-setlevel 1-CONFIG::file::execute=learning
echo 1-CONFIG::file::execute=learning | ccs-loadpolicy -p
See also: Policy File's Modification
You can specify one of modes shown below for functionalities that start with "CONFIG".
Configuration | Meaning |
mode=disabled | Disabled. Works as if regular kernel. |
mode=learning | Learning mode. An access request is not rejected even if the request violates policy. Also, the permission to allow the request is automatically added to policy so that the same request no longer violates policy. |
mode=permissive | Permissive mode. An access request is not rejected even if the request violates policy. But, the permission to allow the request is not added to policy. |
mode=enforcing | Enforcing mode. An access request is rejected if the request violates policy. |
grant_log=yes | Generate grant logs. The max entries are controlled via "max_grant_log=" parameter of "PREFERENCE::audit" line. |
grant_log=no | Don't generate grant logs. |
reject_log=yes | Generate reject logs. The max entries are controlled via "max_reject_log=" parameter of "PREFERENCE::audit" line. |
reject_log=no | Don't generate reject logs. |
Specifies access control level regarding program execution and domain transition.
Specifies access control level regarding domain transition without program execution. (Valid only in 1.7.2 and later.)
Specifies access control level regarding file open for reading and/or writing.
Specifies access control level regarding file create.
Specifies access control level regarding file delete.
Specifies access control level regarding directory create.
Specifies access control level regarding directory delete.
Specifies access control level regarding fifo create.
Specifies access control level regarding UNIX domain socket create.
Specifies access control level regarding file truncate.
Specifies access control level regarding symlink create.
Specifies access control level regarding file overwrite.
Specifies access control level regarding block device file create.
Specifies access control level regarding character device file create.
Specifies access control level regarding link create.
Specifies access control level regarding rename.
Specifies access control level regarding chmod.
Specifies access control level regarding chown.
Specifies access control level regarding chgrp.
Specifies access control level regarding ioctl.
Specifies access control level regarding chroot.
Specifies access control level regarding mount.
Specifies access control level regarding umount.
Specifies access control level regarding pivot_root.
Specifies access control level regarding environment variable names (a.k.a. envp[]).
Specifies access control level regarding creation of TCP sockets.
Specifies access control level regarding TCP socket's listen operation.
Specifies access control level regarding TCP socket's connect operation.
Specifies access control level regarding use of UDP sockets.
Specifies access control level regarding use of RAW sockets.
Specifies access control level regarding use of ROUTE sockets.
Specifies access control level regarding use of PACKET sockets.
Specifies access control level regarding use of create_module(2) init_module(2) delete_module(2) syscall.
Specifies access control level regarding creation of FIFO using mknod(2) syscall.
Specifies access control level regarding creation of block device file using mknod(2) syscall.
Specifies access control level regarding creation of character device file using mknod(2) syscall.
Specifies access control level regarding creation of UNIX domain socket using mknod(2) syscall.
Specifies access control level regarding use of mount(2) syscall.
Specifies access control level regarding use of umount(2) syscall.
Specifies access control level regarding use of reboot(2) syscall.
Specifies access control level regarding use of chroot(2) syscall.
Specifies access control level regarding use of kill(2) tkill(2) tgkill(2) syscall with non 0 signal.
Specifies access control level regarding use of vhangup(2) syscall.
Specifies access control level regarding use of stime(2) settimeofday(2) adjtimex(2) syscall.
Specifies access control level regarding use of nice(2) setpriority(2) syscall.
Specifies access control level regarding use of sethostname(2) setdomainname(2) syscall.
Specifies access control level regarding use of link(2) syscall.
Specifies access control level regarding use of symlink(2) syscall.
Specifies access control level regarding use of rename(2) syscall.
Specifies access control level regarding use of unlink(2) syscall.
Specifies access control level regarding use of chmod(2) fchmod(2) syscall.
Specifies access control level regarding use of chown(2) fchown(2) lchown(2) syscall.
Specifies access control level regarding use of ioctl(2) compat_sys_ioctl(2) syscall.
Specifies access control level regarding use of kexec_load(2) syscall.
Specifies access control level regarding use of pivot_root(2) syscall.
Specifies access control level regarding use of ptrace(2) syscall.
Specifies access control level regarding mount requests that shadow existent mounts.
Specifies access control level regarding UDP socket's local address restriction.
Specifies access control level regarding UDP socket's remote address restriction.
Specifies access control level regarding TCP socket's bind() operation.
Specifies access control level regarding TCP socket's listen() operation.
Specifies access control level regarding TCP socket's connect() operation.
Specifies access control level regarding TCP socket's accept() operation.
Specifies access control level regarding RAW socket's local address restriction.
Specifies access control level regarding RAW socket's remote address restriction.
Specifies access control level regarding signal transmission requests.
Specifies preference on auditing.
"max_grant_log=" limits the max number of grant logs that the kernel can hold.
"max_reject_log=" limits the max number of reject logs that the kernel can hold.
Specifies preference on learning mode.
"verbose=" controls whether to print policy violation messages or not.
"max_entry=" controls the max number of ACL entries that are automatically appended.
"exec.realpath=" controls whether the dereferenced pathname of the requested program should be learned when adding an "allow_execute" entry.
"exec.argv0=" controls whether the invocation name should be learned when adding an "allow_execute" entry.
"symlink.target=" controls whether the content of the symbolic link should be learned when adding an "allow_symlink" entry.
Specifies preference on permissive mode.
"verbose=" controls whether to print policy violation messages or not.
Specifies preference on enforcing mode.
"verbose=" controls whether to print policy violation messages or not.
"penalty=" controls how long (in units of 0.1 second) should the process that violated policy sleep for.
This file contains definition of all domains and permissions that are granted to each domain.
Lines from the next line to a domain definition ( any lines starting with "<kernel>") to the previous line to the next domain definitions are interpreted as access permissions for that domain.
You can specify additional conditions as needed. The syntax for specifying additional conditions are described in Using conditional ACL. Also, how to switch process's state as needed and use the process's state for conditions is described in Using stateful ACL.
To read or modify current domain policy, operate like below.
(Example) Selecting specific domain and appending ACLs. The domain will be created if nonexistent.
printf "<kernel> /sbin/init\nallow_read /etc/passwd\n" | ccs-loadpolicy -d
(Example) Selecting specific domain and appending ACLs. The domain won't be created if nonexistent.
printf "select <kernel> /sbin/init\nallow_read /etc/passwd\n" | ccs-loadpolicy -d
(Example) Selecting specific domain and removing ACLs.
printf "select <kernel> /sbin/init\ndelete allow_read /etc/passwd\ndelete allow_read /etc/shadow\n" | ccs-loadpolicy -d
(Example) Deleting specific domain.
printf "delete <kernel> /sbin/init\n" | ccs-loadpolicy -d
(Example) Reading current domain policy.
cat /proc/ccs/domain_policy
See also: Policy File's Modification
This keyword grants execution of the specified pathname. No wildcards are permitted for the pathname. If you have to use wildcards, use aggregator keyword before granting execute permission using this keyword.
(Example) allow_execute /bin/ls
See also: Domain Transition aggregator
This keyword grants domain transition to the specified pathname without executing program. No wildcards are permitted for the pathname. (Valid only in 1.7.2 and later.)
(Example) allow_transit //app=cgi1\040id=10000
This keyword grants the specified pathname to be opened for writing.
(Example) allow_write /dev/null
See also: path_group
This keyword grants the specified pathname to be opened for reading.
(Example) allow_read /proc/meminfo
See also: path_group
This keyword grants the specified pathname to be opened for reading and writing.
(Example) allow_read/write /dev/null
See also: path_group
This keyword grants the specified pathname to be created.
(Example) allow_create /var/lock/subsys/crond 0644
See also: path_group
This keyword grants the specified pathname to be deleted.
(Example) allow_unlink /var/lock/subsys/crond
See also: path_group
This keyword grants the specified pathname to be created. The pathname must be a directory.
(Example) allow_mkdir /tmp/logwatch.\*/ 0755
See also: path_group
This keyword grants the specified pathname to be deleted. The pathname must be a directory.
(Example) allow_rmdir /tmp/logwatch.\*/
See also: path_group
This keyword grants creation of FIFO by the specified pathname.
(Example) allow_mkfifo /dev/initctl 0600
See also: path_group
This keyword grants creation of UNIX domain socket by the specified pathname.
(Example) allow_mksock /dev/log 0700
See also: path_group
This keyword grants creation of block device file by the specified pathname.
(Example) allow_mkblock /dev/hdc 0640 22 0
See also: path_group
This keyword grants creation of character device file by the specified pathname.
(Example) allow_mkchar /dev/null 0644 1 3
See also: path_group
This keyword grants the specified pathname to be truncated or extended.
(Example) allow_truncate /etc/mtab
See also: path_group
This keyword grants creation of symbolic link by the specified pathname.
(Example) allow_symlink /dev/cdrom
See also: path_group
This keyword grants creation of hard link by the specified pathnames.
(Example) allow_link /etc/mtab~\$ /etc/mtab~
See also: path_group
This keyword grants renaming of the specified pathnames.
(Example) allow_rename /etc/mtab.tmp /etc/mtab
See also: path_group
This keyword grants the specified pathname to be rewritten when the pathname is specified by deny_rewrite keyword.
(Example) allow_rewrite /var/log/messages
See also: path_group deny_rewrite
This keyword grants changing DAC's permissions.
(Example) allow_chmod /dev/mem 0644
See also: path_group number_group
This keyword grants changing DAC's owner.
(Example) allow_chown /dev/sda 0
See also: path_group number_group
This keyword grants changing DAC's group.
(Example) allow_chgrp /dev/audio 92
See also: path_group number_group
This keyword grants doing IOCTL request with the specified command numbers and the specified pathnames.
Example | Permitted access |
allow_ioctl socket:[family=2:type=2:protocol=17] 35093 | Allow sockets with protocol family 2, type 2, protocol 17 to do IOCTL request with command number 35093. |
allow_ioctl /dev/null 10000-20000 | Allow /dev/null to do IOCTL request with command number between 10000 and 20000. |
Regarding the meaning of IOCTL request's command numbers, please refer manuals provided by each module with IOCTL functionality. For example, IOCTL request with command number 21585 means, on i386 platform, FIOCLEX command which turns on the file's close-on-exec flag. For example, IOCTL request with command number 35088 means SIOCGIFNAME command which retrieves the name of network interface.
See also: Using conditional ACL.
To grant mount permission, use allow_mount keyword followed by "$devicefile $mountpoint $filesystem $options". The $devicefile need to be a canonicalized file if the $filesystem requires device file. The $mountpoint must be a canonicalized file. The $options is a hexadecimal integer expression.
To grant "mount -o remount $mountpoint" permission, use allow_mount keyword followed by "any $mountpoint --remount $options".
To grant "mount --bind $source_dir $dest_dir", use "allow_mount $source_dir $dest_dir --bind $options".
To grant "mount --move $source_dir $dest_dir" permission, use "allow_mount $source_dir $dest_dir --move $options".
The $source_dir and $dest_dir must be canonicalized directory.
Kernel 2.6.15 and later supports "Shared Subtree" functionality.
To grant "mount --make-unbindable $mountpoint" permission, use allow_mount keyword followed by "any $mountpoint --make-unbindable $options".
To grant "mount --make-private $mountpoint" permission, use allow_mount keyword followed by "any $mountpoint --make-private $options".
To grant "mount --make-slave $mountpoint" permission, use allow_mount keyword followed by "any $mountpoint --make-slave $options".
To grant "mount --make-shared $mountpoint" permission, use allow_mount keyword followed by "any $mountpoint --make-shared $options".
(Example)
allow_mount none /dev/pts/ devpts 0x0
allow_mount /proc /proc/ proc 0x0
allow_mount usbdevfs /proc/bus/usb/ usbdevfs 0x0
allow_mount none /data/ tmpfs 0xE
allow_mount none /dev/shm/ tmpfs 0xE
allow_mount /dev/hdc /var/www/ ext2 0xF
allow_mount any / --remount 0x0
To grant unmount request, use allow_unmount keyword followed by a canonicalized directory.
(Example)
allow_unmount /mnt/cdrom/
To grant chroot permission, use allow_chroot keyword followed by a canonicalized directory.
Usually, grant /var/empty/sshd/ that sshd uses. In addition, if you have applications that runs in the chroot'ed environment or applications that uses chroot (for example, /usr/share/empty/ is used by vsftpd), grant such directories too.
(Example)
allow_chroot /var/empty/sshd/
allow_chroot /usr/share/empty/
allow_chroot /var/www/html/
allow_chroot /
To grant pivot_root permission, use allow_pivot_root keyword followed by the new root's canonicalized directory and the previous root's canonicalized directory.
Usually, you don't need this keyword.
To restrict the name of environment variables, use allow_env keyword followed by "the name of environment variable".
The execve() system call, which is used to execute a program, accepts filename and argv[] and envp[]. Many programs behave differently depending on envp[].
The purpose of this keyword is to restrict the environment variables passed to an executed programs.
See also: allow_env
To grant capability permission, use allow_capability keyword followed by a capability. The following capabilities are applicable.
allow_capability inet_tcp_create | Permit creation of TCP sockets. |
allow_capability inet_tcp_listen | Permit TCP socket's listen operation. |
allow_capability inet_tcp_connect | Permit TCP socket's connect operation. |
allow_capability use_inet_udp | Permit use of UDP sockets. |
allow_capability use_inet_ip | Permit use of RAW sockets. |
allow_capability use_route | Permit use of ROUTE sockets. |
allow_capability use_packet | Permit use of PACKET sockets. |
allow_capability use_kernel_module | Permit use of create_module(2) init_module(2) delete_module(2) syscall. |
allow_capability create_fifo | Permit creation of FIFO using mknod(2) syscall. |
allow_capability create_block_dev | Permit creation of block device using mknod(2) syscall. |
allow_capability create_char_dev | Permit creation of character device using mknod(2) syscall. |
allow_capability create_unix_socket | Permit creation of UNIX domain sockets using mknod(2) syscall. |
allow_capability SYS_MOUNT | Permit use of mount(2) syscall. |
allow_capability SYS_UMOUNT | Permit use of umount(2) syscall. |
allow_capability SYS_REBOOT | Permit use of reboot(2) syscall. |
allow_capability SYS_CHROOT | Permit use of chroot(2) syscall. |
allow_capability SYS_KILL | Permit use of kill(2) tkill(2) tgkill(2) syscall with non 0 signal. |
allow_capability SYS_VHANGUP | Permit use of vhangup(2) syscall. |
allow_capability SYS_TIME | Permit use of stime(2) settimeofday(2) adjtimex(2) syscall. |
allow_capability SYS_NICE | Permit use of nice(2) setpriority(2) syscall. |
allow_capability SYS_SETHOSTNAME | Permit use of sethostname(2) setdomainname(2) syscall. |
allow_capability SYS_LINK | Permit use of link(2) syscall. |
allow_capability SYS_SYMLINK | Permit use of symlink(2) syscall. |
allow_capability SYS_RENAME | Permit use of rename(2) syscall. |
allow_capability SYS_UNLINK | Permit use of unlink(2) syscall. |
allow_capability SYS_CHMOD | Permit use of chmod(2) fchmod(2) syscall. |
allow_capability SYS_CHOWN | Permit use of chown(2) fchown(2) lchown(2) syscall. |
allow_capability SYS_IOCTL | Permit use of ioctl(2) compat_sys_ioctl(2) syscall. |
allow_capability SYS_KEXEC_LOAD | Permit use of kexec_load(2) syscall. |
allow_capability SYS_PIVOT_ROOT | Permit use of pivot_root(2) syscall. |
allow_capability SYS_PTRACE | Permit use of ptrace(2) syscall. |
allow_capability conceal_mount | Permit use of mount(2) syscall that will hide existing mounts. |
The purpose of allow_capability keyword is to restrict system calls that a program can call. You can restrict more strictly with other keyword and policy files for some system calls.
To grant permission for socket operations, use allow_network keyword followed by protocol(TCP or UDP or RAW) and IP address and port number (for TCP or UDP) / protocol number (for RAW). This permission is applicable to IPv4 and IPv6.
Keyword | Permitted operation | Example |
allow_network TCP bind | Bind to local TCP address/port. | allow_network TCP bind 0.0.0.0 80 |
allow_network TCP listen | Listen to local TCP address/port. | allow_network TCP listen 0.0.0.0 80 |
allow_network TCP accept | Accept from and communicate with remote TCP address/port. | allow_network TCP accept 10.0.0.0-10.255.255.255 1024-65535 |
allow_network TCP connect | Connect to and communicate with remote TCP address/port. | allow_network TCP connect 127.0.0.1 1024-65535 |
allow_network UDP bind | Bind to local UDP address/port. | allow_network UDP bind 0.0.0.0 53 |
allow_network UDP connect | Communicate with remote UDP address/port. | allow_network UDP connect 127.0.0.1 53 |
allow_network RAW bind | Bind to local IP address/protocol. | allow_network RAW bind 127.0.0.1 255 |
allow_network RAW connect | Communicate with remote IP address/protocol. | allow_network RAW connect 10.0.0.1 1 |
Use of "::" for IPv6 address representation is not supported. You need to use "0:0:0:0:0:0:0:1" for "::1".
To reduce the labor of repeating same IP addresses, you can define groups like pathnames.
See also: address_group
To grant permissions for signals, use allow_signal keyword followed by signal number and target domain.
There are two exceptions. If signal number is 0, it is always granted. If the target domain and the source domain are the same, it is always granted.
In other cases, signals are granted only when the signal number matches and the target domain starts with the target domain declared with this keyword.
If only <kernel> is declared as a target domain, the source domain can send signals to any domain with that signal number.
This keyword indicates the profile number currently assigned to this domain. The profile number is an integer between 0 and 255.
This keyword ignores pathnames specified by allow_read keyword in exception policy. You can use this keyword for domains you want to ignore globally readable files.
See also: allow_read
This keyword ignores environment variables names specified by allow_env keyword in exception policy. You can use this keyword for domains you want to ignore globally acceptable environment variable names.
See also: allow_env
This domain executes only one program specified by this keyword. You can use this keyword for domains you want to validate parameters before executing the requested program.
If this keyword is specified, only one program specified by this keyword regardless of the mode specified by CONFIG::file::execute. Thus, if the pathname specified by this program cannot be executed, no programs can be executed from this domain.
See also: denied_execute_handler CONFIG::file::execute allow_execute
This domain executes this program only when execute request was rejected and the mode of CONFIG::file::execute is enforcing. If this keyword is not specified and the mode of CONFIG::file::execute is enforcing, execute request is rejected.
Exception is, if the execute_handler keyword is specified, denied_execute_handler keyword is ignored.
This keyword indicates that this domain has failed to append entry in learning mode since the number of entries reached to the limit specified by PREFERENCE::learning keyword. You need to reduce the number of entries for this domain by tuning policy.
See also: PREFERENCE::learning
This keyword indicates that some process in this domain was not able to transit to new domain when processing the execute request.
If this domain was assigned a profile with CONFIG::file::execute=enforcing , the execute request was rejected.
Otherwise, the execute request was not rejected. In that case, the process continued execution without domain transition. Since the reason of transition failure is either "the name of the domain was too long" or "the kernel was unable to allocate memory", you need to consider "suppressing domain transitions" or "increasing memory quota" if you are planning to assign a profile with CONFIG::file::execute=enforcing to this domain.
See also: keep_domain Memory Usage Information
To read or modify current exception policy, operate like below.
(Example)
echo 'file_pattern /proc/\$/status' | ccs-loadpolicy -e
echo 'delete file_pattern /proc/\$/status' | ccs-loadpolicy -e
cat /proc/ccs/exception_policy
See also: Policy File's Modification
To declare pathname pattern, use file_pattern keyword followed by pathname pattern. The pathname pattern must be a canonicalized Pathname. This keyword is not applicable to neither granting execute permissions nor domain definitions.
For example, canonicalized pathname that contains a process ID (i.e. /proc/PID/ files) needs to be grouped in order to make access control work well.
To declare pathname group, use path_group keyword followed by name of the group and pathname pattern.
For example, if you want to group all files under home directory, you can define
path_group HOME-DIR-FILE /home/\*/\* path_group HOME-DIR-FILE /home/\*/\{\*\}/\* |
in the exception policy and use like
allow_read @HOME-DIR-FILE |
to grant file access permission.
To declare number group, use number_group keyword followed by name of the group and number ranges.
For example, if you want to group 0644 and 0664, you can define
number_group CREATE_MODES 0644 number_group CREATE_MODES 0664 |
in the exception policy and use like
allow_create /tmp/file @CREATE_MODES |
to grant access permission.
To declare address group, use address_group keyword followed by name of the group and IP address pattern.
For example, if you want to group all local addresses, you can define
address_group local-address 10.0.0.0-10.255.255.255 address_group local-address 172.16.0.0-172.31.255.255 address_group local-address 192.168.0.0-192.168.255.255 |
in the exception policy and use like
allow_network TCP accept @local-address 1024-65535 |
to grant network access permission.
To grant unconditionally readable permissions, use allow_read keyword followed by canonicalized file. This keyword is intended to reduce size of domain policy by granting read access to library files such as GLIBC and locale files. Exception is, if ignore_global_allow_read keyword is given to a domain, entries specified by this keyword are ignored.
See also: allow_read ignore_global_allow_read
To grant unconditionally acceptable environment variables' names, use allow_env keyword followed by the name of environment variables. This keyword is intended to reduce memory usage for commonly granted environment variables (e.g. PATH and HOME). You should not grant dangerous environment variables like LD_PRELOAD . Exception is, if ignore_global_allow_env keyword is given to a domain, entries specified by this keyword are ignored.
See also: ignore_global_allow_env allow_env
To deny overwriting already written contents of file (such as log files) by default, use deny_rewrite keyword followed by pathname pattern. Files whose pathname match the patterns are not permitted to open for writing without append mode or truncate unless the pathnames are explicitly granted using allow_rewrite keyword in domain policy.
See also: allow_rewrite
To deal multiple programs as a single program, use aggregator keyword followed by name of original program and aggregated program. This keyword is intended to aggregate similar programs.
For example, /usr/bin/tac and /bin/cat are similar. By specifying "aggregator /usr/bin/tac /bin/cat", you can run /usr/bin/tac in the domain for /bin/cat .
For example, /usr/sbin/logrotate for Fedora Core 3 generates programs like /tmp/logrotate.\?\?\?\?\?\? and run them, but TOMOYO Linux doesn't allow using patterns for granting execute permission and defining domains. By specifying "aggregator /tmp/logrotate.\?\?\?\?\?\? /tmp/logrotate.tmp", you can run /tmp/logrotate.\?\?\?\?\?\? as if /tmp/logrotate.tmp is running.
See also: allow_execute
To initialize domain transition when specific program is executed, use initialize_domain directive.
If the part "from" and after is not given, the entry is applied to all domain. If the "domain" doesn't start with "<kernel>", the entry is applied to all domain whose domainname ends with "the last program part of domain".
This directive is intended to aggregate domain transitions for daemon program and program that are invoked by the kernel on demand, by transiting to different domain.
See also: Domain Transition no_initialize_domain
To deny the effect of "initialize_domain" directive, use "no_initialize_domain" directive.
Use this directive when you don't want to initialize domain transition.
See also: Domain Transition initialize_domain
To prevent domain transition when program is executed from specific domain, use keep_domain directive.
If the part "from" and before is not given, this entry is applied to all program. If the "domain" doesn't start with "<kernel>", the entry is applied to all domain whose domainname ends with "the last program part of domain".
This directive is intended to reduce total number of domains and memory usage by suppressing unneeded domain transitions.
See also: Domain Transition no_keep_domain
To deny the effect of "keep_domain" directive, use "no_keep_domain" directive.
Use this directive when you want to escape from a domain that is kept by "keep_domain" directive.
See also: Domain Transition keep_domain
To prevent specific local port from being selected automatically, use deny_autobind keyword followed by local port number.
This keyword is intended to prevent specific local port from being bound for temporary use. For example, some proxy server uses local port 8080, so port 8080 should not be bound by other programs for temporary use.
(Example)
deny_autobind 1-1023
deny_autobind 8080
This file is used to manually grant or reject individual access requests when the policy violation occurs in enforcing mode. If a policy violation occur in a process whose domain is assigned a profile for enforcing mode, the administrator can judge interactively using "ccs-queryd" command.
This file is used to read or append the list of programs or domains that can write to /proc/ccs/ interface.
By default, only processes with both UID = 0 and EUID = 0 can modify policy via /proc/ccs/ interface. You can use this keyword to allow policy modification by non root user.
This is a view (of a DBMS) that contains only profile number and domainnames of domain so that "ccs-setprofile" command can do line-oriented processing easily.
This file is to show the total RAM used to keep policy in the kernel by TOMOYO Linux.
(Example)
cat /proc/ccs/meminfo
This file holds the granted log. The reader process returns immediately if no granted logs exists. To wait until a granted log is generated, use select(2) for readability. The max number of logs that the kernel can hold is limited to max_grant_log parameter of PREFERENCE::audit, so read out timely.
(Example)
cat /proc/ccs/grant_log
This file holds the rejected log. The reader process returns immediately if no violation logs exists. To wait until a violation log is generated, use select(2) for readability. The max number of logs that the kernel can hold is limited to max_reject_log parameter of PREFERENCE::audit, so read out timely.
(Example)
cat /proc/ccs/reject_log
This file is to show the name of domain the caller process belongs to.
(Example)
cat /proc/ccs/self_domain
This file is used by "ccs-pstree" command to show "list of processes currently running" and "domains which each process belongs to" and "profile number which the domain is currently assigned" like "pstree" command. This file is writable by programs that aren't registered as policy manager.
This file is used for getting TOMOYO Linux's version.
(Example)
cat /proc/ccs/version
By default, only processes with both UID = 0 and EUID = 0 can modify policy via /proc/ccs/ interface. But if you want to permit policy modification via /proc/ccs/ interface by non root user, you can write this keyword like
# echo manage_by_non_root | /usr/sbin/ccs-loadpolicy -m |
to disable UID and EUID checks. Also, you can write this keyword like
# echo delete manage_by_non_root | /usr/sbin/ccs-loadpolicy -m |
to enable UID and EUID checks again. Use chown/chmod as needed since the owner of /proc/ccs/ interface is root.
To be able to do this steps, /sbin/ccs-init also executes /etc/ccs/ccs-post-init if /etc/ccs/ccs-post-init is executable. Therefore, to allow access to /proc/ccs/ interface by user demo, create /etc/ccs/ccs-post-init with
#! /bin/sh echo manage_by_non_root > /proc/ccs/manager chown -R demo /proc/ccs/ |
and initialize like
# chmod 755 /etc/ccs/ccs-post-init # chown -R demo /etc/ccs/ |
Then, user demo will be able to access policy directories and policy editors.
You can add conditions (e.g. UID and GID) as needed. The condition clause are appended to the tail of each permission using "if" directive.
Example | Meaning |
allow_read /etc/passwd | Allow opening /etc/passwd for reading. |
allow_read /etc/passwd if task.uid=0 | Allow opening /etc/passwd for reading only if the process's UID is 0. |
allow_read /etc/passwd if task.uid!=0 | Allow opening /etc/passwd for reading only if the process's UID is not 0. |
allow_network TCP connect 10.0.0.1 80 | Allow connecting TCP socket to 10.0.0.1 port 80. |
allow_network TCP connect 10.0.0.1 80 if task.uid=100 | Allow connecting TCP socket to 10.0.0.1 port 80 only if the process's UID is 100. |
allow_capability SYS_KILL | Allow using kill(2) syscall. |
allow_capability SYS_KILL if task.ppid=1 task.uid=0 task.euid=0 | Allow using kill(2) syscall only if the parent process is /sbin/init and the process's UID is 0 and the process's EUID is 0. |
The following variables are available.
Variable | Meaning |
task.uid | UID of current process |
task.euid | Effective UID of current process |
task.suid | Saved UID of current process |
task.fsuid | File System UID of current process |
task.gid | GID of current process |
task.egid | Effective GID of current process |
task.sgid | Saved GID of current process |
task.fsgid | File System GID of current process |
task.pid | PID of current process |
task.ppid | PID of parent process |
path1.uid | UID of object. |
path1.gid | GID of object. |
path1.ino | i-node number of object. |
path1.parent.uid | UID of object's parent directory. |
path1.parent.gid | GID of object's parent directory. |
path1.parent.ino | i-node number of object's parent directory. |
path2.parent.uid | UID of object's parent directory. |
path2.parent.gid | GID of object's parent directory. |
path2.parent.ino | i-node number of object's parent directory. |
"path1" corresponds to the first pathname of operations that requires pathnames, and "path2" corresponds to the second pathname of operations that requires pathnames. For example, the case of "allow_rename file1 file2", path1 corresponds to file1 and path2 corresponds to file2.
"path1" except "path1.parent" is not available for pathnames that don't exist. Thus, you can't use when creating pathnames (such as allow_create keyword).
"path1.parent" is always available.
"path2.parent" is available only for operations that require 2 pathnames (i.e. allow_link and allow_rename keywords).
"path2" is available only for mount operations.
"path1" is not supported when accessing via "sysctl" (i.e. accessing files under /proc/sys/ directories using "sysctl" instead for "open").
Variable | Meaning |
exec.realpath | Dereferenced pathname of the requested program. |
exec.argc | Number of argv[] passed for execute request. |
exec.envc | Number of envp[] passed for execute request. |
Condition | Meaning |
exec.realpath="value" | Dereferenced pathname of the requested program matches "value". |
exec.realpath!="value" | Dereferenced pathname of the requested program does not match "value". |
exec.argv[index]="value" | argv[index] (where 0 <= index < exec.argc) matches "value". |
exec.argv[index]!="value" | argv[index] (where 0 <= index < exec.argc) does not match "value". |
exec.envp["name"]="value" | Environment variable "name" is defined and matches "value". |
exec.envp["name"]!="value" | Environment variable "name" is not defined or does not match "value". |
exec.envp["name"]!=NULL | Environment variable "name" is defined. |
exec.envp["name"]=NULL | Environment variable "name" is not defined. |
Condition | Meaning |
task.type=execute_handler | Current process is a program specified by execute_handler keyword. |
task.type!=execute_handler | Current process is not a program specified by execute_handler keyword. |
Condition | Meaning |
path1.type=file | path1 is a regular file. |
path1.type=directory | path1 is a directory. |
path1.type=fifo | path1 is a FIFO. |
path1.type=socket | path1 is a socket. |
path1.type=symlink | path1 is a symbolic link. |
path1.type=block | path1 is a block device file. |
path1.type=char | path1 is a character device file. |
path1.type!=file | path1 is not a regular file. |
path1.type!=directory | path1 is not a directory. |
path1.type!=fifo | path1 is not a FIFO. |
path1.type!=socket | path1 is not a socket. |
path1.type!=symlink | path1 is not a symbolic link. |
path1.type!=block | path1 is not a block device file. |
path1.type!=char | path1 is not a character device file. |
Since path1.parent and path2.parent are always directory, TOMOYO Linux does not support path1.parent and path2.parent for type of file.
Condition | Meaning |
path1.major=num1-num2 | Device major number of a device file which path1 resides is between num1 and num2. |
path1.minor=num1-num2 | Device minor number of a device file which path1 resides is between num1 and num2. |
path1.major!=num1-num2 | Device major number of a device file which path1 resides is not between num1 and num2. |
path1.minor!=num1-num2 | Device minor number of a device file which path1 resides is not between num1 and num2. |
Since a device file where path1.parent and path2.parent reside is always same as the device file where path1 resides (because cross device operation is not permitted), TOMOYO Linux does not support path1.parent and path2.parent for device numbers.
If num1 and num2 is the same value, you can omit -num2 part.
Condition | Meaning |
path1.dev_major=num1-num2 | Device file's major number is between num1 and num2. |
path1.dev_minor=num1-num2 | Device file's minor number is between num1 and num2. |
path1.dev_major!=num1-num2 | Device file's major number is not between num1 and num2. |
path1.dev_minor!=num1-num2 | Device file's minor number is not between num1 and num2. |
These conditions are valid only for path1.type=block or path1.type=char cases.
If num1 and num2 is the same value, you can omit -num2 part.
Condition | Meaning |
path1.perm=num1-num2 | path1's permission is between num1 and num2. |
path1.perm!=num1-num2 | path1's permission is not between num1 and num2. |
path1.perm=setuid | path1's setuid bit is on. |
path1.perm!=setuid | path1's setuid bit is off. |
path1.perm=setgid | path1's setgid bit is on. |
path1.perm!=setgid | path1's setgid bit is off. |
path1.perm=sticky | path1's sticky bit is on. |
path1.perm!=sticky | path1's sticky bit is off. |
path1.perm=owner_read | path1's owner read bit is on. |
path1.perm!=owner_read | path1's owner read bit is off. |
path1.perm=owner_write | path1's owner write bit is on. |
path1.perm!=owner_write | path1's owner write bit is off. |
path1.perm=owner_execute | path1's owner execute bit is on. |
path1.perm!=owner_execute | path1's owner execute bit is off. |
path1.perm=group_read | path1's group read bit is on. |
path1.perm!=group_read | path1's group read bit is off. |
path1.perm=group_write | path1's group write bit is on. |
path1.perm!=group_write | path1's group write bit is off. |
path1.perm=group_execute | path1's group execute bit is on. |
path1.perm!=group_execute | path1's group execute bit is off. |
path1.perm=others_read | path1's others read bit is on. |
path1.perm!=others_read | path1's others read bit is off. |
path1.perm=others_write | path1's others write bit is on. |
path1.perm!=others_write | path1's others write bit is off. |
path1.perm=others_execute | path1's others execute bit is on. |
path1.perm!=others_execute | path1's others execute bit is off. |
These conditions are applicable for path1.parent and path2.parent as well as path1 .
If num1 and num2 is the same value, you can omit -num2 part.
To specify value in octal format, start from 0 (e.g. path1.perm=0644 ).
will allow opening /dev/null for reading and writing only if /dev/null's type is character device file and /dev/null's major number is 1 and /dev/null's minor number is 3 and /dev/null's permission is 0666.
Condition | Meaning |
symlink.target="value" | The content of a symlink to be created matches "value". |
symlink.target!="value" | The content of a symlink to be created does not match "value". |
Since TOMOYO Linux doesn't require modification of userland applications, it is impossible to change the range of accessible resources without domain transition. But there are cases you wish to change the range of accessible resources without domain transition. For example, you might wish to change the range of accessible resources depending on the client's IP address. To support such cases, you can assign state variables to each processes and you can use the state variables in Using conditional ACL.
Variable | Meaning |
task.state[0] | Current process's state variable 0 |
task.state[1] | Current process's state variable 1 |
task.state[2] | Current process's state variable 2 |
Each task.state can take an integer value between 0 and 255. To change state variable, append " ; set " part after an ACL.
Example | Meaning |
allow_network TCP accept @TRUSTED_HOSTS 1024-65535 ; set task.state[0]=1 | If a TCP connection is accepted from an IP address included in an address group @TRUSTED_HOSTS, set 1 to state[0]. |
allow_network TCP accept @UNTRUSTED_HOSTS 1024-65535 ; set task.state[0]=0 | If a TCP connection is accepted from an IP address included in an address group @UNTRUSTED_HOSTS, set 0 to state[0]. |
allow_execute /bin/bash if task.state[0]=1 | Allow execution of /bin/bash if state[0] is 1. |
allow_execute /sbin/nologin if task.state[0]=0 | Allow execution of /sbin/nologin if state[0] is 0. |
allow_execute /etc/passwd if task.state[2]=0 ; set task.state[2]=1 | If state[2] is 0, allow opening /etc/passwd for reading and then, set 1 to state[2]. |
When using the state variables, please be careful with the following points.
You can make the process which violated policy in enforcing mode sleep for specified period.
Example of /proc/ccs/profile | Meaning |
3-PREFERENCE::enforcing={ penalty=1 } | Make the process which violated policy in enforcing mode and which belongs to a domain with profile 3 sleep for 0.1 second. |
4-PREFERENCE::enforcing={ penalty = 10 } | Make the process which violated policy in enforcing mode and which belongs to a domain with profile 4 sleep for 1 second. |
This feature is a safeguard to avoid that the CPU usage remains 100% when policy violation occurs in an infinite loop. Usually, making processes sleep for 0.1 second is enough.
This feature is not applied against network's receive operation so that attackers cannot make services sleep for long time (in other words, delay your system's response) by intentionally sending TCP connection requests and UDP packets from unwanted sources.
Basically, TOMOYO Linux controls whether to execute a program or not according to the domain policy. You can check parameters using exec.argv and exec.envp described in Using conditional ACL. But this approach support only simple pattern matching and you need to specify what programs are permitted to be executed beforehand.
Therefore, TOMOYO Linux supports a mechanism named execute_handler. If this mechanism is used, the kernel no longer controls whether to execute a requested program or not, and the kernel merely executes the program specified by execute_handler, and the program specified by execute_handler determines whether to execute the requested program or not, and the program specified by execute_handler executes the requested program only if the program specified by execute_handler considers it is appropriate.
In Linux, the behavior "execute a program" means "overwrite the process which requested to execute a program with the requested program's image" and "the process which requested to execute a program cannot regain control if the execute request was succeeded". Therefore, the process which requested to execute a program can receive a notification only when the execute request was failed.
For example, let's consider a situation where a process running as program-A attempts to execute program-B.
When the process running as program-A requests the execution of program-B, the kernel checks the domain policy for "whether it is appropriate to execute program-B from a process running as program-A or not" and the kernel overwrites the process running as program-A with program-B if the kernel considers it is appropriate, and the kernel doesn't overwrite the process running as program-A with program-B and notifies the process running as program-A that execution of program-B is not permitted if the kernel considers it is not appropriate.
When execute_handler is specified, a different program program-C specified as execute_handler mediates this behavior.
When the process running as program-A requests the execution of program-B, the kernel overwrites the process running as program-A with program-C to let the program-C judge whether it is appropriate to execute program-B from a process running as program-A or not.
The process now running as program-C determines whether it is appropriate to execute program-B from a process running as program-A or not, and the process now running as program-C requests the execution of program-B (and the kernel will overwrite the process now running as program-C with program-B) if the process now running as program-C considers it is appropriate, and the process now running as program-C terminates without executing program-B if the process now running as program-C considers it is not appropriate.
As stated above, this mechanism has a side effect that it becomes impossible to notify the process running as program-A that the requested program (i.e. program-B) was not executed since program-C abandons a mean to notify the process running as program-A that the execute request of program-B was not accepted.
But, even if execute_handler is not specified, there are various factors that cause "the execute request was accepted but the program terminated before starting the expected behavior" such as "the process was unable to read shared libraries", "the process received KILL signal", "the system became out of memory and the process was killed by OOM killer". In other words, there are uncertainties between "the execute request did not fail" and "the executed program starts the expected behavior".
Viewing in this light, there is no guarantee that "the program starts expected behavior unless the process receives a notification that the execution of the program failed" from the beginning. And, it is possible to say that it is an acceptable result that the program-C specified by execute_handler failed to notify the process previously running as program-A that the execution of program-B failed.
TOMOYO Linux's assumes that the administrator knows what programs needs to be executed from what programs beforehand and permits execution of minimal programs. Thus, assuming that unexpected execute request which are not permitted by policy won't occur as long as the system is running properly, it is OK to accept all execute requests. If an execute request that should not be accepted occurs, you can take different actions such as terminating the process instead of rejecting the request by using denied_execute_handler mechanism. So, you don't have to let the kernel judge whether to execute the program or not alone.
Thus, you can let external userland program judge whether to execute the requested program or not occurred from a domain by specifying execute_handler keyword to the domain.
If you try to judge inside the kernel, there are few library functions available and it is more likely to fail when allocating contiguous memory area. But if you try to judge outside the kernel, there are many library functions available and it is less likely to fail when allocating contiguous memory area, and you can do more detailed checking. So, you can let external userland program specified by execute_handler keyword examine parameters and let the program execute the requested program only if parameters are appropriate.
The side effect of this approach is that there is no mean to notify the process that the execute request was not accepted when it is not appropriate to execute the requested program. But since you can freely customize the program for execute_handler keyword, you can even judge using ssh to ask remotely.
To use this feature, specify like below.
Example of /proc/ccs/domain_policy | Meaning |
execute_handler /usr/sbin/check-and-exec | Whenever a process which belongs to this domain requests execution of a program, execute /usr/sbin/check-and-exec instead for the requested program. /usr/sbin/check-and-exec checks parameters and executes the requested program if /usr/sbin/check-and-exec considers it is appropriate to execute. |
The program specified by execute_handler keyword receives the following parameters. Compare with allow_execute log described in Access Logs.
Be careful with the following notes when you use this feature.
A source code named audit-exec-param.c is included as a sample program of how to use this mechanism in the ccs-tools source package. You can customize freely.
This mechanism is just providing a hook. How to utilize this hook is up to you.
TOMOYO Linux's approach is "know what programs needs to be executed from what programs beforehand and create policy that permits execution of minimal programs". Thus, you can not only reject unnecessary execution requests but also do different behavior.
By default, if an execute request of a program which is not permitted by allow_execute keyword occurs in enforcing mode, the kernel rejects the execute request. But assuming that you know what programs needs to be executed from what programs beforehand, an execute request of a program which is not permitted by allow_execute keyword will not occur as long as the process is keeping control, and you can regard that the process is not keeping control (in other words, the process already lost control) if such request occurs.
Attackers steal control of a process by attacking security holes such as buffer overflow and attempt to execute commands such as shells. If the process does not need to execute the shell (in other words, you needn't to give permission like "allow_execute /bin/bash"), it is considered that the process has already lost control at the moment of the execution request of shells.
Normally, when execution of a program which is not permitted by the policy is requested, the kernel merely reject the request. But it is unlikely that the process gets back control (in other words, the process resumes proper operations) by just rejecting the request if the request is issued by the process that has lost control.
In Linux, "execute a program" means that the current process is overwritten by the requested program and transfer control to the requested program. This means that a process gets back control by overwriting the process with different program even if the process has lost control because of buffer overflow.
The control of a process which has once lost control by the attacker and is overwritten by a program requested by the attacker depends on the program used for overwriting. If a program like shells is executed, the control remains on the attacker's side (in other words, the owner of the process) because shells accept whatever the user requested. But if a program which terminates silently (e.g. /bin/true) is executed, the control will not remains on the attacker's side because the process owned by the attacker will terminate immediately.
As described above, an event that "an execute request of an unnecessary program is issued by an attacker" depending on how you look at it. You can consider that "the attacker is giving the system a chance to get back control on the system's side".
Thus, TOMOYO Linux provides a mechanism that executes different program instead of merely rejecting the request when an execute request of a program which is not permitted by policy occurs. How to utilize this mechanism is up to you.
For example, you can replace the execute request of a program which is not permitted by the policy with /bin/true so that the process which requested the execution of a program which is not permitted by the policy will terminate immediately.
For example, you can replace the execute request of shells with a honey pot client's program and observe what requests the attacker issues.
For example, you can forcibly terminate the login session.
For example, you can show warning message like "You are not permitted to execute this program." which is similar to Ubuntu's command-not-found package (which tells the user in what package the requested command is included).
For example, you can change a firewall's configuration if you succeeded to derive the IP address of the attacker.
To use this feature, specify like below.
Example of /proc/ccs/profile | Example of /proc/ccs/domain_policy | Meaning |
3-CONFIG::file::execute=enforcing | use_profile 3 denied_execute_handler /bin/true | If a process which belongs to a domain with profile 3 requested execution of a program which is not permitted by the domain policy, execute /bin/true instead of rejecting the execute request. |
Notes on this feature is the same as Judging execute request outside the kernel.