Posts for Thursday, May 23, 2013

avatar

A SELinux policy for incron: the basic skeleton

So, in the previous post I talked about incron and why I think moving it into the existing cron policy would not be a good idea. It works, somewhat, but is probably not that future-proof. So we’re going to create our own policy for it.

In SELinux, policies are generally written through 3 files:

  1. a type enforcement file that contains the SELinux rules applicable to the domain(s) related to the application (in our example, incron)
  2. a file context file that tells the SELinux utilities how the files and directories offered by the application should be labeled
  3. an interface definition file that allows other SELinux policy modules to gain rights offered through the (to be written) incron policy

We now need to create a skeleton for the policy. This skeleton will define the types related to the application. Such types can be the domains for the processes (the context of the incrond and perhaps also incrontab applications), the contexts for the directories (if any) and files, etc.

So let’s take a look at the content of the incron package. On Gentoo, we can use qlist incron for this. In the output of qlist, I added comments to show you how contexts can be (easily) deduced.

# Application binary for managing user crontabs. We want to give this a specific
# context because we want the application (which will manage the incrontabs in
# /var/spool/incron) in a specific domain
/usr/bin/incrontab  ## incrontab_exec_t

# General application information files, do not need specific attention
# (the default context is fine)
/usr/share/doc/incron-0.5.10/README.bz2
/usr/share/doc/incron-0.5.10/TODO.bz2
/usr/share/doc/incron-0.5.10/incron.conf.example.bz2
/usr/share/doc/incron-0.5.10/CHANGELOG.bz2
/usr/share/man/man8/incrond.8.bz2
/usr/share/man/man5/incron.conf.5.bz2
/usr/share/man/man5/incrontab.5.bz2
/usr/share/man/man1/incrontab.1.bz2

# Binary for the incrond daemon. This definitely needs its own context, since
# it will be launched from an init script and we do not want it to run in the
# initrc_t domain.
/usr/sbin/incrond ## incrond_exec_t

# This is the init script for the incrond daemon. If we want to allow 
# some users the rights to administer incrond without needing to grant
# those users the sysadm_r role, we need to give this file a different
# context as well.
/etc/init.d/incrond ## incrond_initrc_exec_t

With this information at hand, and the behavior of the application we know from the previous post, can lead to the following incron.fc file, which defines the file contexts for the application.

/etc/incron.d(/.*)?     gen_context(system_u:object_r:incron_spool_t,s0)

/etc/rc\.d/init\.d/incrond      --      gen_context(system_u:object_r:incrond_initrc_exec_t,s0)

/usr/bin/incrontab      --      gen_context(system_u:object_r:incrontab_exec_t,s0)

/usr/sbin/incrond       --      gen_context(system_u:object_r:incrond_exec_t,s0)

/var/spool/incron(/.*)?         gen_context(system_u:object_r:incron_spool_t,s0)

The syntax of this file closely follows the syntax that semanage fcontext takes – at least for the regular expressions in the beginning. The last column is specifically for policy development to generate a context based on the policies’ requirements: an MCS/MLS enabled policy will get the trailing sensitivity with it, but when MCS/MLS is disabled then it is dropped. The middle column is to specify if the label should only be set on regular files (--), directories (-d), sockets (-s), symlinks (-l), etc. If it is omitted, it matches whatever class the path matches.

The second file needed for the skeleton is the incron.te file, which would look like so. I added in inline comments here to explain why certain lines are prepared, but generally this is omitted when the policy is upstreamed.

policy_module(incron, 0.1)
# The above line declares that this file is a SELinux policy file. Its name
# is incron, so the file should saved as incron.te

# First, we declare the incrond_t domain, used for the "incrond" process.
# Because it is launched from an init script, we tell the policy that
# incrond_exec_t (the context of incrond), when launched from init, should
# transition to incrond_t.
#
# Basically, the syntax here is:
# type <domain>
# type <file context="context">
# <interface call="call" linking="linking" the="the" two="two">
type incrond_t;
type incrond_exec_t;
init_daemon_domain(incrond_t, incrond_exec_t)

# Next we declare that the incrond_initrc_exec_t is an init script context
# so that init can execute it (remember, SELinux is a mandatory access control
# system, so if we do not tell that init can execute it, it won't).
type incrond_initrc_exec_t;
init_script_file(incrond_initrc_exec_t)

# We also create the incrontab_t domain (for the "incrontab" application), which
# is triggered through the incrontab_exec_t labeled file. This again follows a bit
# the syntax as we used above, but now the interface call is "application_domain".
type incrontab_t;
type incrontab_exec_t;
application_domain(incrontab_t, incrontab_exec_t)

# Finally we declare the spool type as well (incron_spool_t) and tell SELinux that
# it will be used for regular files.
type incron_spool_t;
files_type(incron_spool_t)

Knowing which interface calls, like init_daemon_domain and application_domain, we should use is not obvious at first. Most of this can be gathered from existing policies. Other frequently occurring interfaces to be used immediately at the skeleton side are (examples for a foo_t domain):

  • logging_log_file(foo_log_t) to inform SELinux that the context is used for logging purposes. This allows generic log-related daemons to do “their thing” with the file.
  • files_tmp_file(foo_tmp_t) to identify the context as being used for temporary files
  • files_tmpfs_file(foo_tmpfs_t) for tmpfs files (which could be shared memory)
  • files_pid_file(foo_var_run_t) for PID files (and other run metadata files)
  • files_config_file(foo_conf_t) for configuration files (often within /etc)
  • files_lock_file(foo_lock_t) for lock files (often within /run/lock)

We might be using these later as we progress with the policy (for instance, the PID file is a very high candidate for needing to be included). However, with the information currently at hand, we have our first policy module ready for building. Save the type enforcement rules in incron.te and the file contexts in incron.fc and you can then build the SELinux policy:

# make -f /usr/share/selinux/strict/include/Makefile incron.pp
# semodule -i incron.pp

On Gentoo, you can then relabel the files and directories offered through the package using rlpkg:

# rlpkg incron

Next is to start looking at the incrontab application.

Posts for Wednesday, May 22, 2013

avatar

A SELinux policy for incron: what does it do?

In this series of posts, we’ll go through the creation of a SELinux policy for incron, a simple inotify based cron-like application. I will talk about the various steps that I would take in the creation of this policy, and give feedback when certain decisions are taken and why. At the end of the series, we’ll have a hopefully well working policy.

The first step in developing a policy is to know what the application does and how/where it works. This allows us to check if its behavior matches an existing policy (and as such might be best just added to this policy) or if a new policy needs to be written. So, what does incron do?

From the documentation, we know that incron is a cron-like application that, unlike cron, works with file system notification events instead of time-related events. Other than that, it uses a similar way of working:

  • A daemon called incrond is the run-time application that reads in the incrontab files and creates the proper inotify watches. When a watch is triggered, it will execute the matching rule.
  • The daemon looks at two definitions (incrontabs): one system-wide (in /etc/incron.d) and one for users (in /var/spool/incron).
  • The user tabfiles are managed through incrontab (the command)
  • Logging is done through syslog
  • User commands are executed with the users’ privileges (so the application calls setuid() and setgid())

With this, one can create a script to be executed when a file is uploaded (or deleted) to/from a file server, or when a process coredump occurred, or whatever automation you want to trigger when some file system event occurred. Events are plenty and can be found in /usr/include/sys/inotify.h.

So, with this information, it is safe to assume that we might be able to push incron in the existing cron policy. After all, it defines the contexts for all these and probably doesn’t need any additional tweaking. And this seems to work at first, but a few tests reveal that the behavior is not that optimal.

# chcon -t crond_exec_t /usr/sbin/incrond
# chcon -t crontab_exec_t /usr/bin/incrontab
# chcon -R -t system_cron_spool_t /etc/incron.d
# chcon -t cron_log_t /var/log/cron.log
# chcon -R -t cron_spool_t /var/spool/incron

System tables work somewhat, but all commands are executed in the crond_t domain, not in a system_cronjob_t or related domain.
User tables fail when dealing with files in the users directories, since these too run in crond_t and thus have no read access to the user home directories.

The problems we notice come from the fact that the application is very simple in its code: it is not SELinux-aware (so it doesn’t change the runtime context) as most cron daemons are, and when it changes the user id it does not call PAM, so we cannot trigger pam_selinux.so to handle context changes either. As a result, the entire daemon keeps running in crond_t.

This is one reason why a separate domain could be interesting: we might want to extend the rights of the daemon domain a bit, but don’t want to extend these rights to the other cron daemons (who also run in crond_t). Another reason is that the cron policy has a few booleans that would not affect the behavior at all, making it less obvious for users to troubleshoot. As a result, we’ll go for the separate policy instead – which will be for the next post.

Posts for Tuesday, May 21, 2013

avatar

Why oh why does a process run in unlabeled_t?

If you notice that a process is running in the unlabeled_t domain, the first question to ask is how it got there.

Well, one way is to have a process running in a known domain, like screen_t, after which the SELinux policy module that provides this domain is removed from the system (or updated and the update does not contain the screen_t definition anymore):

test ~ # ps -eZ | grep screen
root:sysadm_r:sysadm_screen_t    5047 ?        00:00:00 screen
test ~ # semodule -r screen
test ~ # ps -eZ | grep screen
system_u:object_r:unlabeled_t    5047 ?        00:00:00 screen

In permissive mode, this will be visible easily; in enforcing mode, the domains you are running in might not be allowed to do anything with unlabeled_t files, directories and processes, so ps might not show it even though it still exists:

test audit # ps -eZ | grep 5047
test audit # ls -dZ /proc/5047
ls: cannot access /proc/5047: Permission denied
test audit # tail audit.log | grep unlabeled
type=AVC msg=audit(1368698097.494:27806): avc:  denied  { getattr } for  pid=4137 comm="bash" path="/proc/5047" dev="proc" ino=6677 scontext=root:sysadm_r:sysadm_t tcontext=system_u:object_r:unlabeled_t tclass=dir

Notice that, if you reload the module, the process becomes visible again. That is because the process context itself (screen_t) is retained, but because the policy doesn’t know it anymore, it shows it as unlabeled_t.

Basically, the moment the policy doesn’t know how a label would be (should be), it uses unlabeled_t. The SELinux policy then defines how this unlabeled_t domain is handled. Processes getting into unlabeled_t is not that common though as there is no supported transition to it. The above one is one way that this still can occur.

Posts for Monday, May 20, 2013

“Host your own” is cynical

It seems as if Yahoo will soon be the proud owner of the Tumbleblogging platform Tumblr (they paid a whopping 1.1 Billion USD for it). We’ve seen many of those acquisitions in the last years, Facebook bought Instagram, Google bought Youtube, Microsoft bought Skype, AOL bought ICQ (yeah this one is for the old farts like me). You could go on just summarizing those deals for quite a while.

The reaction throughout the community always tends to be the same:

367312 original 300x292 Host your own is cynical

Apart from a general preference for the small companies, the cool startups, the underdogs, every big company has screwed almost everyone in some way. They killed some service, they changed the TOS or something else about the service1. But to be fair: The track record of big companies buying smaller ones is horrible. The once cool and/or edgy services we love get reamed between corporate interests and strategies until the  thing we liked no longer exists. So a good deal of skepticism seems to be in order.

After the first wave of “NOOOOOOO” we tend to get the usual blog posts about “hosting stuff yourself”.  I’ll try to summarize all of those blogposts words in a few words:

Relying on someone else to host your content? You are stupid. Just buy your own server, set up a web and a database server  and just run it all yourself. Here’s some code (which you cannot reasonably discern from a prayer to Cthulhu) .  It will  download some beatific package  onto your box and after incanting the following 37 other commands on your machine and investing about a week diving through howtos an manuals (each with at least one typo or mistake in them for fun and education) you might get your blog up and running to post something. Then you are self reliant and a useful citizen of the Internet. Running a mail server comes next.

Of course my description is highly biased and a little bit unfair (but where would be the fun in doing it otherwise). No wait, we call it ‘slightly exaggerated to make a point’. That sounds better.

Hosting your infrastructure seems to resonate well with the whole DIY/maker movement we’ve been seeing lately: You can get all the tools and information to build basically everything online. So learn to sow, learn to solder, learn to tame a mountain lion!

But there’s one thing people proposing this solution tend to forget: You are not the baseline. With the Internet having become mainstream most users don’t actually know how it works. Many don’t even know what “hosting” means, what a server is and why “The thingy that looks like a red furry thingy flying around the earth” is actually called “browser” and not “the Internet”. We ridicule them, refer to their problems as PEBKAC (Problem exists between keyboard and chair). We laugh at their incompetence, their blogs with flashy animated GIFs hosted on some ridiculously bad platform. And then we talk to our friends, all skilled and informed enough to run their own server, with enough time and money to keep in running, about how important running your own crap is. And they nod knowingly. Splendid strategy.

We live in a society based in the so-called division of labor: We are all specialists in something and completely clueless in other areas that still affect our lives in a big way. I for example know a lot about computers and software but I have no idea about cars and how to fix them. I don’t know gardening. I can cook but I can’t make my own clothes. Sure, if I stop doing the things I like (like writing this or building software) I could learn how to make my own shirts. But why? Others do it better and me buying their product offers them a chance to make a living (I tend to buy the majority of my clothes from fair trade companies). So why would I settle with shitty shirts I made myself?

Making things easier to self-host is great and while it has had (and still has) its problems, Software like WordPress has made it a lot easier for many to self host. But that’s still far away from it being mainstream. And do we really want the world back where only the few people with the resources and skills can publish? I don’t think so.

Telling people to “host your own” when some big company closes or buys a service is very similar to the princess who, when learning that the peasants had no bread, said: “Let them eat cake.”

Hosting your own is a solution for the gifted and wealthy few, for many it’s blatant cynicism. You want to help people who have to rely on the big providers to host their posts, on some startup that is just waiting to be bought by one of the big players?

Don’t tell them to host their own, find ways to get their stuff hosted aside from economical interests. Form collectives that host people’s blogs, find new ways to break the dependency of the general public on very few corporations. Everything else is just you telling the world how great you are and that your own skill set is the one skill set everybody needs.

  1. and change is almost generally considered to be bad, until people get used to it

The post “Host your own” is cynical appeared first on tante.blog.

flattr this!

avatar

A simple IPv6 setup

For internal communication between guests on my workstation, I use IPv6 which is set up using the Router Advertisement “feature” of IPv6. The tools I use are dnsmasq for DNS/DHCP and router advertisement support, and dhcpcd as client. It might be a total mess (grew almost organically until it worked), but as far as I’m concerned, it is working… and that is all that matters (for now). I’ll have to look deeper into the IPv6 stuff to understand it all better though.

On the client side, dhcpcd is ran with the following options:

dhcpcd_eth0="-t 5 -L --ipv6ra_own"

I had to enable --ipv6ra_own to get it to obtain its global address, otherwise it only got its link local one (fe80:: something). I also added a hook into /lib/dhcpcd/dhcpcd-hooks to get it to trigger a hostname update for IPv6.

$ cat 28-set-ip6-address 
if $ifup; then export new_ip_address=${ra1_prefix%%/64}; fi

SELinux-policy wise, I had to enable dhcpc_t to write to the hostname proc file and set the system hostname. The first one (21) is needed because of the --ipv6ra_own parameter.

# selocal -l | grep dhcpc_t
21: allow dhcpc_t self:rawip_socket create_socket_perms; # dhcpclient
22: kernel_rw_kernel_sysctl(dhcpc_t) # set hostname
23: allow dhcpc_t self:capability sys_admin; # set hostname

Finally, in /etc/dhcpcd.conf, I removed the nohook lookup-hostname and set the force_hostname one:

#nohook lookup-hostname
env force_hostname=YES

On the server side, I use the following configuration of dnsmasq (snippet):

dhcp-range=2001:db8:81:e2::,ra-only
enable-ra
dhcp-option=option6:dns-server,[2001:db8:81:e2::26b5:365b:5072]

As you can see, I use the documentation prefix for now (since it is meant for internal communication only, and makes it easier to copy/paste into documentation ;-) but when I am going to use full IPv6 access to the Internet, this prefix will of course change.

Finally, I enabled IPv6 forwarding on the tap0 interface because otherwise I continuously got the following messages on the clients:

May 12 18:43:07 test dhcpcd[3869]: eth0: adding default route via fe80::d848:19ff:fe0d:55c2
May 12 18:43:07 test dhcpcd[3869]: eth0: fe80::d848:19ff:fe0d:55c2 is no longer a router
May 12 18:43:07 test dhcpcd[3869]: eth0: deleting default route via fe80::d848:19ff:fe0d:55c2
May 12 18:43:13 test dhcpcd[3869]: eth0: fe80::d848:19ff:fe0d:55c2 is unreachable, expiring it

To enable IPv6 forwarding, you can use sysctl but I added it in the script that sets up the tap0 interface:

tunctl -b -u swift -t tap0
ifconfig tap0 add 2001:db8:81:e2::26b5:365b:5072/64
vde_switch --numports 16 --mod 777 --group users --tap tap0 -d
echo 1 > /proc/sys/net/ipv6/conf/tap0/forwarding

Posts for Sunday, May 19, 2013

avatar

The weird “audit_access” permission

While writing up the posts on capabilities, one thing I had in my mind was to give some additional information on frequently occurring denials, such as the dac_override and dac_read_search capabilities, and when they are triggered. For the DAC-related capabilities, policy developers often notice that these capabilities are triggered without a real need for them. So in the majority of cases, the policy developer wants to disable auditing of this:

dontaudit <somedomain> self:capability { dac_read_search dac_override };

When applications wants to search through directories not owned by the user as which the application runs, both capabilities will be checked – first the dac_read_search one and, if that is denied (it will be audited though) then dac_override is checked. If that one is denied as well, it too will be audited. That is why many developers automatically dontaudit both capability calls if the application itself doesn’t really need the permission.

Let’s say you allow this because the application needs it. But then another issue comes up when the application checks file attributes or access permissions (which is a second occurring denial that developers come across with). Such applications use access() or faccessat() to get information about files, but other than that don’t do anything with the files. When this occurs and the domain does not have read, write or execute permissions on the target, then the denial is shown even when the application doesn’t really read, write or execute the file.

#include <stdio.h>
#include <unistd.h>

int main(int argc, char ** argv) {
  printf("%s: Exists (%d), Readable (%d), Writeable (%d), Executable (%d)\n", argv[1],
    access(argv[1], F_OK), access(argv[1], R_OK),
    access(argv[1], W_OK), access(argv[1], X_OK));
}
$ check /var/lib/logrotate.status
/var/lib/logrotate.status: Exists (0), Readable (-1), Writeable (-1), Executable (-1)

$ tail -1 /var/log/audit.log
...
type=AVC msg=audit(1367400559.273:5224): avc:  denied  { read } for  pid=12270 comm="test" name="logrotate.status" dev="dm-3" ino=2849 scontext=staff_u:staff_r:staff_t tcontext=system_u:object_r:logrotate_var_lib_t tclass=file

This gives the impression that the application is doing nasty stuff, even when it is merely checking permissions. One way would be to dontaudit read as well, but if the application does the check against several files of various types, that might mean you need to include dontaudit statements for various domains. That by itself isn’t wrong, but perhaps you do not want to audit such checks but do want to audit real read attempts. This is what the audit_access permission is for.

The audit_access permission is meant to be used only for dontaudit statements: it has no effect on the security of the system itself, so using it in allow statements has no effect. The purpose of the permission is to allow policy developers to not audit access checks without really dontauditing other, possibly malicious, attempts. In other words, checking the access can be dontaudited while actually attempting to use the access (reading, writing or executing the file) will still result in the proper denial.

Posts for Saturday, May 18, 2013

avatar

Commandline SELinux policy helper functions

To work on SELinux policies, I use a couple of functions that I can call on the shell (command line): seshowif, sefindif, seshowdef and sefinddef. The idea behind the methods is that I want to search (find) for an interface (if) or definition (def) that contains a particular method or call. Or, if I know what the interface or definition is, I want to see it (show).

For instance, to find the name of the interface that allows us to define file transitions from the postfix_etc_t label:

$ sefindif filetrans.*postfix_etc
contrib/postfix.if: interface(`postfix_config_filetrans',`
contrib/postfix.if:     filetrans_pattern($1, postfix_etc_t, $2, $3, $4)

Or to show the content of the corenet_tcp_bind_http_port interface:

$ seshowif corenet_tcp_bind_http_port
interface(`corenet_tcp_bind_http_port',`
        gen_require(`
                type http_port_t;
        ')

        allow $1 http_port_t:tcp_socket name_bind;
        allow $1 self:capability net_bind_service;
')

For the definitions, this is quite similar:

$ sefinddef socket.*create
obj_perm_sets.spt:define(`create_socket_perms', `{ create rw_socket_perms }')
obj_perm_sets.spt:define(`create_stream_socket_perms', `{ create_socket_perms listen accept }')
obj_perm_sets.spt:define(`connected_socket_perms', `{ create ioctl read getattr write setattr append bind getopt setopt shutdown }')
obj_perm_sets.spt:define(`create_netlink_socket_perms', `{ create_socket_perms nlmsg_read nlmsg_write }')
obj_perm_sets.spt:define(`rw_netlink_socket_perms', `{ create_socket_perms nlmsg_read nlmsg_write }')
obj_perm_sets.spt:define(`r_netlink_socket_perms', `{ create_socket_perms nlmsg_read }')
obj_perm_sets.spt:define(`client_stream_socket_perms', `{ create ioctl read getattr write setattr append bind getopt setopt shutdown }')

$ seshowdef manage_files_pattern
define(`manage_files_pattern',`
        allow $1 $2:dir rw_dir_perms;
        allow $1 $3:file manage_file_perms;
')

I have these defined in my ~/.bashrc (they are simple functions) and are used on a daily basis here ;-) If you want to learn a bit more on developing SELinux policies for Gentoo, make sure you read the Gentoo Hardened SELinux Development guide.

Posts for Friday, May 17, 2013

avatar

Looking at the local Linux kernel privilege escalation

There has been a few posts already on the local Linux kernel privilege escalation, which has received the CVE-2013-2094 ID. arstechnica has a write-up with links to good resources on the Internet, but I definitely want to point readers to the explanation that Brad Spengler made on the vulnerability.

In short, the vulnerability is an out-of-bound access to an array within the Linux perf code (which is a performance measuring subsystem enabled when CONFIG_PERF_EVENTS is enabled). This subsystem is often enabled as it offers a wide range of performance measurement techniques (see its wiki for more information). You can check on your own system through the kernel configuration (zgrep CONFIG_PERF_EVENTS /proc/config.gz if you have the latter pseudo-file available – it is made available through CONFIG_IKCONFIG_PROC).

The public exploit maps memory in userland, fills it with known data, then triggers an out-of-bound decrement that tricks the kernel into decrementing this data (mapped in userland). By looking at where the decrement occurred, the exploit now knows the base address of the array. Next, it targets (through the same vulnerability) the IDT base (Interrupt Descriptor Table) and targets the overflow interrupt vector. It increments the top part of the address that the vector points to (which is 0xffffffff, becoming 0×00000000 thus pointing to the userland), maps this memory region itself with shellcode, and then triggers the overflow. The shell code used in the public exploit modifies the credentials of the current task, sets uid/gid with root and gives full capabilities, and then executes a shell.

As Brad mentions, UDEREF (an option in a grSecurity enabled kernel) should mitigate the attempt to get to the userland. On my system, the exploit fails with the following (start of) oops (without affecting the system further) when it tries to close the file descriptor returned from the syscall that invokes the decrement:

[ 1926.226678] PAX: please report this to pageexec@freemail.hu
[ 1926.227019] BUG: unable to handle kernel paging request at 0000000381f5815c
[ 1926.227019] IP: [<ffffffff811016ba>] sw_perf_event_destroy+0x1a/0xa0
[ 1926.227019] PGD 58a7c000 
[ 1926.227019] Thread overran stack, or stack corrupted
[ 1926.227019] Oops: 0002 [#4] PREEMPT SMP 
[ 1926.227019] Modules linked in: libcrc32c
[ 1926.227019] CPU 0 
[ 1926.227019] Pid: 4267, comm: test Tainted: G      D      3.8.7-hardened #1 Bochs Bochs
[ 1926.227019] RIP: 0010:[<ffffffff811016ba>]  [<ffffffff811016ba>] sw_perf_event_destroy+0x1a/0xa0
[ 1926.227019] RSP: 0018:ffff880058a03e08  EFLAGS: 00010246
...

The exploit also finds that the decrement didn’t succeed:

test: semtex.c:76: main: Assertion 'i<0x0100000000/4' failed.

A second mitigation is that KERNEXEC (also offered through grSecurity) which prevents the kernel from executing data that is writable (including userland data). So modifying the IDT would be mitigated as well.

Another important mitigation is TPE – Trusted Path Execution. This feature prevents the execution of binaries that are not located in a root-owned directory and owned by a trusted group (which on my system is 10 = wheel). So users attempting to execute such code will fail with a Permission denied error, and the following is shown in the logs:

[ 3152.165780] grsec: denied untrusted exec (due to not being in trusted group and file in non-root-owned directory) of /home/user/test by /home/user/test[bash:4382] uid/euid:1000/1000 gid/egid:100/100, parent /bin/bash[bash:4352] uid/euid:1000/1000 gid/egid:100/100

However, even though a nicely hardened system should be fairly immune against the currently circling public exploit, it should be noted that it is not immune against the vulnerability itself. The methods above mentioned make it so that that particular way of gaining root access is not possible, but it still allows an attacker to decrement and increment memory in specific locations so other exploits might be found to modify the system.

Now out-of-bound vulnerabilities are not new. Recently (february this year), a vulnerability in the networking code also provided an attack vector to get a local privilege escalation. A mandatory access control system like SELinux has little impact on such vulnerabilities if you allow users to execute their own code. Even confined users can modify the exploit to disable SELinux (since the shell code is ran with ring0 privileges it can access and modify the SELinux state information in the kernel).

Many thanks to Brad for the excellent write-up, and to the Gentoo Hardened team for providing the grSecurity PaX/TPE protections in its hardened-sources kernel.

Posts for Thursday, May 16, 2013

avatar

Gentoo Hardened spring notes

We got back together on the #gentoo-hardened chat channel to discuss the progress of Gentoo Hardened, so it’s time for another write-up of what was said.

Toolchain

GCC 4.8.1 will be out soon, although nothing major has occurred with it since the last meeting. There is a plugin header install problem in 4.8 and its not certain that the (trivial) fix is in 4.8.1, but it certainly is inside Gentoo’s release.

Blueness is also (still, and hopefully for a long time ;-) maintaining the uclibc hardened related toolchain aspects.

Kernel and grSecurity/PaX

The further progress on the XATTR_PAX migration was put on a lower level the past few weeks due to busy, busy… very busy weeks (but this was announced and known in advance). We still need to do XATTR copying in install for packages that do pax markings before src_install() and include the user.pax XATTR patch in the gentoo-sources kernel. This will silence the errors for non-hardened users and fix the loss of XATTR markings for those packages that do pax-mark before install.

The set then needs to be documented further and tested on vanilla and hardened systems.

Zorry asked if a separate script can be provided for those ebuilds that directly call paxctl. These ebuilds might want to switch to the eclass, but if they need to call paxctl or similar directly (for instance because the result is immediately used for further building), a separate script or tool should be made available. Blueness will look into this.

On hardened-sources, we are now with stable 2.6.32-r160, 3.2.42-r1 and 3.8.6 due to some vulnerabilities in earlier versions (in networking code). There is still some bug (nfs-related) that is fixed in 3.2.44 so that part might need a bump as well soon.

SELinux

The selocal command is now available for Gentoo SELinux users, allowing them to easily enhance the policy without having to maintain their own SELinux policy modules (the script is a wrapper that does all that).

The setools package now also uses the SLOT’ed swig, so no more dependency breakage.

On SELinux userspace and policy, both have seen a new release last month, and both are already in the Gentoo portage tree.

Finally, the SELinux policy ebuilds now also call epatch_user so users can customize the policies even further without having to copy ebuilds to their overlay.

Now that tar supports XATTR well, we might want to look into SELinux stages again. Jmbsvicetto did some work on that, but the builds failed during stage1. We’ll look into that later.

Integrity

Nothing much to say, we’re waiting a bit until the patches proposed by the IMA team are merged in the main kernel.

Profiles

Two no-multilib fixes have been applied to the hardened/amd64/no-multilib profiles. One was a QA issue and quickly resolved, the other is due to the profile stacking within Gentoo profiles, where we missed a profile and thus were missing a few masks defined in that (missed) profile. But including the profile creates a lot of duplicates again, so we are going to copy the masks across until the duplicates are resolved in the other profiles.

Blueness will also clean up the experimental 13.0 directory since all hardened profiles now follow 13.0.

Docs

The latest changes on SELinux have been added to the Gentoo SELinux handbook. Also, I’ve been slowly (but surely) adding topics to the SELinux tutorials listing on the Gentoo wiki.

The grSecurity 2 document is very much out of date, blueness hopes to put some time in fixing that soon.

So that’s about it for the short write-up. Zorry will surely post the log later on the appropriate channels. Good work done (again) by all team members!

Programming is Terrible

<iframe class="youtube-player" frameborder="0" height="390" src="http://www.youtube.com/embed/csyL9EC0S0c?version=3&amp;rel=1&amp;fs=1&amp;showsearch=0&amp;showinfo=1&amp;iv_load_policy=1&amp;wmode=transparent" type="text/html" width="640"></iframe>


Video on GObject bindings and Vala

<iframe class="youtube-player" frameborder="0" height="390" src="http://www.youtube.com/embed/6QrGmA_RR4E?version=3&amp;rel=1&amp;fs=1&amp;showsearch=0&amp;showinfo=1&amp;iv_load_policy=1&amp;wmode=transparent" type="text/html" width="640"></iframe>

Tal Liron explaining how to generate
bindings (GObjectInstrospection bindings) for Vala


Paludis 1.4.0 Released

Paludis 1.4.0 has been released:

  • Tweaked ‘cave resolve’ output to add blank lines.
  • Support for libarchive 3.1.2.
  • Compatibility fixes for GCC 4.8.

Filed under: paludis releases Tagged: paludis
avatar

Public support channels: irc

I’ve said it before – support channels for free software are often (imo) superior to the commercial support that you might get with vendors. And although those vendors often try to use “modern” techniques, I fail to see why the old, but proven/stable methods would be wrong.

Consider the “Chat with Support” feature that many vendors have on their site. Often, these services use a webbrowser, AJAX-driven method for talking with support engineers. The problem with this that I see is that it is difficult to keep track of the feedback you got over time (unless you manually copy/paste the information), and again that it isn’t public. With free software communities, we still often redirect such “online” support requests to IRC.

Internet Relay Chat has been around for ages (1988 according to wikipedia) and still quite active. Gentoo has all of its support channels on the freenode IRC network: a community-driven, active #gentoo channel with often crosses the 1000 users, a #gentoo-dev development-related channel where many developers communicate, the #gentoo-hardened channel for all questions and support regarding Gentoo Hardened specifics, etc.

Using IRC has many advantages. One is that logs can be kept (either individually or by the project itself) that can be queried later by the people who want to provide support (to see if questions have already been popping up, see what the common questions are for the last few days, etc.) or get support (to see if their question was already answered in the past). Of course, these logs can be made public through web interfaces quite easily. For users, such log functionality is offered through the IRC client. Another very simple, yet interesting feature is highlighting: give the set of terms for which you want to be notified (usually through a highlight and a specific notification in the client), making it easier to be on multiple channels without having to constantly follow-up on all discussions.

Another advantage is that there is such a thing like “bots”. Most Gentoo related channels do not allow active bots on the channels except for the project-approved ones (such as willikens). These bots can provide project-specific help to users and developers alike:

  • Give one-line information about bugs reported on bugzilla (id, assignee, status, but also the URL where the user/developer can view the bug etc.)
  • Give meta information about a package (maintainer, herd, etc.), herd (members), GLSA details, dependency information, etc.
  • Allow users to query if a developer is away or not
  • Create notes (messages) for users that are not online yet but for which you know they come online later (and know their nickname or registered username)
  • Notify when commits are made, or when tweets are sent that match a particular expression, etc.

Furthermore, the IRC protocol has many features that are very interesting to use in free software communities as well. You can still do private chats (when potentially confidential data is exchanged) for instance, or even exchange files (although that is less common to use in free software communities). There is also still some hierarchy in case of abuse (channel operators can remove users from the chat or even ban them for a while) and one can even quiet a channel when for instance online team meetings are held (although using a different channel for that might be an alternative).

IRC also has the advantage that connecting to the IRC channels has a very low requirement (software-wise): one can use console-only chat clients (in case users cannot get their graphical environment to work – example is irssi) or even webbrowser based ones (if one wants to chat from other systems). Even smartphones have good IRC applications, like AndChat for Android.

IRC is also distributed: an IRC network consists of many interconnected servers who pass on all IRC traffic. If one node goes down, users can access a different node and continue. That makes IRC quite high-available. IRC network operators do need to try and keep the network from splitting (“netsplit”) which occurs when one part of the distributed network gets segregated from the other part and thus two “independent” IRC networks are formed. When that occurs, IRC operators will try to join them back as fast as possible. I’m not going to explain the details on this – it suffices to understand that IRC is a distributed manner and thus often much more available than the “support chat” sites that vendors provide.

So although IRC looks archaic, it is a very good match for support channel requirements.

Posts for Wednesday, May 15, 2013

avatar

Overriding the default SELinux policies

Extending SELinux policies with additional rules is easy. As SELinux uses a deny by default approach, all you need to do is to create a policy module that contains the additional (allow) rules, load that and you’re all set. But what if you want to remove some rules?

Well, sadly, SELinux does not support deny rules. Once an allow rule is loaded in memory, it cannot be overturned anymore. Yes, you can disable the module itself that provides the rules, but you cannot selectively disable rules. So what to do?

Generally, you can disable the module that contains the rules you want to disable, and load a custom module that defines everything the original module did, except for those rules you don’t like. For instance, if you do not want the skype_t domain to be able to read/write to the video device, create your own skype-providing module (myskype) with the exact same content (except for the module name at the first line) as the original skype module, except for the video device:

dev_read_sound(skype_t)
# dev_read_video_dev(skype_t)
dev_write_sound(skype_t)
# dev_write_video_dev(skype_t)

Load in this policy, and you now have the skype_t domain without the video access. You will get post-install failures when Gentoo pushes out an update to the policy though, since it will attempt to reload the skype.pp file (through the selinux-skype package) and fail because it declares types and attributes already provided (by myskype). You can exclude the package from being updated, which works as long as no packages depend on it. Or live with the post-install failure ;-) But there might be a simpler approach: epatch_user.

Recently, I added in support for epatch_user in the policy ebuilds. This allows users to create patches against the policy source code that we use and put them in /etc/portage/patches in the directory of the right category/package. For module patches, the working directory used is within the policy/modules directory of the policy checkout. For base, it is below the policy checkout (in other words, the patch will need to use the refpolicy/ directory base). But because of how epatch_user works, any patch taken from the base will work as it will start stripping directories up to the fourth one.

This approach is also needed if you want to exclude rules from interfaces rather than from the .te file: create a small patch and put it in /etc/portage/patches for the sec-policy/selinux-base package (as this provides the interfaces).

Posts for Tuesday, May 14, 2013

avatar

Highlevel assessment of Cdorked and Gentoo Hardened/SELinux

With all the reports surrounding Cdorked, I took a look at if SELinux and/or other Gentoo Hardened technologies could reduce the likelihood that this infection occurs on your system.

First of all, we don’t know yet how the malware gets installed on the server. We do know that the Apache binaries themselves are modified, so the first thing to look at is to see if this risk can be reduced. Of course, using an intrusion detection system like AIDE helps, but even with Gentoo’s qcheck command you can test the integrity of the files:

# qcheck www-servers/apache
Checking www-servers/apache-2.2.24 ...
  * 424 out of 424 files are good

If the binary is modified, this would result in something equivalent to:

Checking www-servers/apache-2.2.24 ...
 MD5-DIGEST: /usr/sbin/apache2
  * 423 out of 424 files are good

I don’t know if the modified binary would otherwise work just fine, I have not been able to find exact details on the infected binary to (in a sandbox environment of course) analyze this further. Also, because we don’t know how they are installed, it is not easy to know if binaries that you built yourself are equally likely to be modified/substituted or if the attack checks checksums of the binaries against a known list.

Assuming that it would run, then the infecting malware would need to set the proper SELinux context on the file (if it overwrites the existing binary, then the context is retained, otherwise it gets the default context of bin_t). If the context is wrong, then starting Apache results in:

apache2: Syntax error on line 61 of /etc/apache2/httpd.conf: Cannot load /usr/lib64/apache2/modules/mod_actions.so into server: /usr/lib64/apache2/modules/mod_actions.so: cannot open shared object file: Permission denied

This is because the modified binary stays in the calling domain context (initrc_t). If you use a targeted policy, then this will not present itself as initrc_t is an unconfined domain. But with strict policies, initrc_t is not allowed to read httpd_modules_t. Even worse, the remainder of SELinux protections don’t apply anymore, since with unconfined domains, all bets are off. That is why Gentoo focuses this hard on using a strict policy.

So, what if the binary runs in the proper domain? Well then, from the articles I read, the malware can do a reverse connect. That means that the domain will attempt to connect to an IP address provided by the attacker (in a specifically crafted URL). For SELinux, this means that the name_connect permission is checked:

# sesearch -s httpd_t -c tcp_socket -p name_connect -ACTS
Found 20 semantic av rules:
   allow nsswitch_domain dns_port_t : tcp_socket { name_connect } ; 
DT allow httpd_t port_type : tcp_socket { name_connect } ; [ httpd_can_network_connect ]
DT allow httpd_t ftp_port_t : tcp_socket { name_connect } ; [ httpd_can_network_relay ]
DT allow httpd_t smtp_port_t : tcp_socket { name_connect } ; [ httpd_can_sendmail ]
DT allow httpd_t postgresql_port_t : tcp_socket { name_connect } ; [ httpd_can_network_connect_db ]
DT allow httpd_t oracledb_port_t : tcp_socket { name_connect } ; [ httpd_can_network_connect_db ]
DT allow httpd_t squid_port_t : tcp_socket { name_connect } ; [ httpd_can_network_relay ]
DT allow httpd_t mssql_port_t : tcp_socket { name_connect } ; [ httpd_can_network_connect_db ]
DT allow httpd_t kerberos_port_t : tcp_socket { name_connect } ; [ allow_kerberos ]
DT allow nsswitch_domain ldap_port_t : tcp_socket { name_connect } ; [ authlogin_nsswitch_use_ldap ]
DT allow httpd_t http_cache_port_t : tcp_socket { name_connect } ; [ httpd_can_network_relay ]
DT allow httpd_t http_port_t : tcp_socket { name_connect } ; [ httpd_can_network_relay ]
DT allow httpd_t http_port_t : tcp_socket { name_connect } ; [ httpd_graceful_shutdown ]
DT allow httpd_t mysqld_port_t : tcp_socket { name_connect } ; [ httpd_can_network_connect_db ]
DT allow httpd_t ocsp_port_t : tcp_socket { name_connect } ; [ allow_kerberos ]
DT allow nsswitch_domain kerberos_port_t : tcp_socket { name_connect } ; [ allow_kerberos ]
DT allow httpd_t pop_port_t : tcp_socket { name_connect } ; [ httpd_can_sendmail ]
DT allow nsswitch_domain ocsp_port_t : tcp_socket { name_connect } ; [ allow_kerberos ]
DT allow httpd_t gds_db_port_t : tcp_socket { name_connect } ; [ httpd_can_network_connect_db ]
DT allow httpd_t gopher_port_t : tcp_socket { name_connect } ; [ httpd_can_network_relay ]

So by default, the Apache (httpd_t) domain is allowed to connect to DNS port (to resolve hostnames). All other name_connect calls depend on SELinux booleans (mentioned after it) that are by default disabled (at least on Gentoo). Disabling hostname resolving is not really feasible, so if the attacker uses a DNS port as port that the malware needs to connect to, SELinux will not deny it (unless you use additional networking constraints).

Now, the reverse connect is an interesting feature of the malware, but not the main one. The main focus of the malware is to redirect customers to particular sites that can trick the user in downloading additional (client) malware. Because this is done internally within Apache, SELinux cannot deal with this. As a user, make sure you configure your browser not to trust non-local iframes and such (always do this, not just because there is a possible threat right now). The configuration of Cdorked is a shared memory segment of Apache itself. Of course, since Apache uses shared memory, the malware embedded within will also have access to the shared memory. However, if this shared memory would need to be accessed by third party applications (the malware seems to grant read/write rights on everybody to this segment) SELinux will prevent this:

# sesearch -t httpd_t -c shm -ACTS
Found 2 semantic av rules:
   allow unconfined_domain_type domain : shm { create destroy getattr setattr read write associate unix_read unix_write lock } ; 
   allow httpd_t httpd_t : shm { create destroy getattr setattr read write associate unix_read unix_write lock } ; 

Only unconfined domains and the httpd_t domain itself have access to httpd_t labeled shared memory.

So what about IMA/EVM? Well, those will not help here since IMA checks for integrity of files that were modified offline. As the modification of the Apache binaries is most likely done online, IMA would just accept this.

For now, it seems that a good system integrity approach is the most effective until we know more about how the malware-infected binary is written to the system in the first place (as this is better protected by MAC controls like SELinux).

Posts for Monday, May 13, 2013

avatar

SECMARK and SELinux

When using SECMARK, the administrator configures the iptables or netfilter rules to add a label to the packet data structure (on the host itself) that can be governed through SELinux policies. Unlike peer labeling, here the labels assigned to the network traffic is completely locally defined. Consider the following command:

# iptables -t mangle -A INPUT -p tcp --src 192.168.1.2 --dport 443
  -j SECMARK --selctx system_u:object_r:myauth_packet_t

With this command, packets that originate from the 192.168.1.2 host and arrive on port 443 (typically used for HTTPS traffic) are marked as myauth_packet_t. SELinux policy writers can then allow domains to receive this type of packets (or send) through the packet class:

# Allow sockets with mydomain_t context to receive packets labeled myauth_packet_t
allow mydomain_t myauth_packet_t:packet recv;

The SELinux policy modules enable this through the corenet_sendrecv_<type>_{client,server}_packets interfaces:

corenet_sendrecv_http_client_packets(mybrowser_t)
# allow mybrowser_t http_client_packet_t:packet { send recv };

As a common rule, packets are marked as client packets or server packets, depending on the role of the domain. In the above example, the domain is a browser, so acts as a web client. So, it needs to send and receive http_client_packet_t. A web server on the other hand would need to send and receive http_server_packet_t. Note that the packets that are sent over the wire do not have any labels assigned to them – this is all local to the system. So even when the source and destination use SELinux with SECMARK, on the source server the packets might be labeled as http_client_packet_t whereas on the target they are seen as http_server_packet_t.

As far as I know, when you want to use SECMARK, you will need to set the contexts with iptables yourself (there is no default labeling), so knowing about the above convention is important.

Again, Paul Moore has more information about this.

Posts for Sunday, May 12, 2013

avatar

Peer labeling in SELinux policy

Allow me to start with an important warning: I don’t have much hands-on experience with the remainder of this post. Its based on the few resources I found on the Internet and a few tests done locally which I’ve investigated in my attempt to understand SELinux policy writing for networking stuff.

So, with that out of the way, let’s look into peer labeling. As mentioned in my previous post, SELinux supports some more advanced networking security features than the default socket restrictions. I mentioned SECMARK and NetLabel before, but NetLabel is actually part of the family of peer labeling technologies.

With this technology approach, all participating systems in the network must support the same labeling method. NetLabel supports CIPSO (Commerial IP Security Option) where hosts label their network traffic to be part of a particular “Domain of Interpretation”. The labels are used by the hosts to identify where a packet should be for. NetLabel, within Linux, is then used to translate those CIPSO labels. SELinux itself labels the incoming sockets based on the NetLabel information and the context of the listening socket, resulting in a context that is governed policy-wise through the peer class. Since this is based on the information in the packet instead of defined on the system itself, this allows remote systems to have a say in how the packets are labeled.

Another peer technology is the Labeled IPSec one. In this case the labels are fully provided by the remote system. I think they are based on the security association within the IPSec setup.

In both cases, in the SELinux policies, three definitions are important to keep an eye out on: interface definitions, node definitions and peer definitions.

Interface definitions allow users to (mainly) set the sensitivity that is allowed to pass the interface. Using semanage interface this can be controlled by the user. One can also assign a different context to the interface – by default, this is netif_t. The permissions that are checked on the traffic is ingress (incoming) and egress (outgoing) traffic, and most policies set this through the following call (comment shows the underlying SELinux rules, where tcp_send and tcp_recv are – I think – obsolete):

corenet_tcp_sendrecv_generic_if(something_t)
# allow something_t netif_t:netif { tcp_send tcp_recv egress ingress };

Node definitions define which targets (nodes, which can be IP addresses or subnets) traffic meant for a particular socket is allow to originate from (recvfrom) or sent to (sendto). Again, users can define their own node types and manage them using semanage node. The default node I already covered in the previous post (node_t) and is allowed by most policies by default through the following call (where the tcp_send and tcp_recv are probably deprecated as well):

corenet_tcp_sendrecv_generic_node(something_t)
# allow something_t node_t:node { tcp_send tcp_recv sendto recvfrom };

Finally, peer definitions are based on the labels from the traffic. If the system uses NetLabel, then the target label will always be netlabel_peer_t since the workings of CIPSO are mainly (only?) mapped towards sensitivity labels (in MLS policy). As a result, SELinux always displays the peer as being netlabel_peer_t. In case of Labeled IPSec, this isn’t the case as the peer label is transmitted by the peer itself.

For NetLabel support, policies generally include two methods – one is to support unlabeled traffic (only needed the moment you have support for labeled traffic) and one is to allow the NetLabel’ed traffic:

corenet_all_recvfrom_unlabeled(something_t)
# allow something_t unlabeled_t:peer recv;
corenet_all_recvfrom_netlabel(something_t)
# allow something_t netlabel_peer_t:peer recv;

In case of IPSec for instance, the peer will have a provided label, as is shown by the call for accepting hadoop traffic:

hadoop_recvfrom(something_t)
# allow something_t hadoop_t:peer recv;

However, this alone is not sufficient for labeled IPSec. We also need to allow the domain to be allowed to send anything towards an IPSec security association. There is an interface called corenet_tcp_recvfrom_labeled that takes two arguments which, amongst other things, enables sendto towards its association.

corenet_tcp_recvfrom_labeled(some_t, thing_t)
# allow { some_t thing_t} self:association sendto;
# allow some_t thing_t:peer recv;
# allow thing_t some_t:peer recv;
# corenet_tcp_recvfrom_netlabel(some_t)
# corenet_tcp_recvfrom_netlabel(thing_t)

This interface is usually called within a *_tcp_connect() interface for a particular domain, like with the mysql_tcp_connect example:

interface(`mysql_tcp_connect',`
        gen_require(`
                type mysqld_t;
        ')

        corenet_tcp_recvfrom_labeled($1, mysqld_t)
        corenet_tcp_sendrecv_mysqld_port($1) # deprecated
        corenet_tcp_connect_mysqld_port($1)
        corenet_sendrecv_mysqld_client_packets($1)
')

When using peer labeling, the domain that is allowed something is based on the socket context of the application. Also, the rules when using peer labeling are in addition to the rules mentioned before (“standard” networking control): name_bind and name_connect are always checked.

For more information, make sure you check Paul Moore’s blog, such as the egress/ingress information. And if you know of resources that show this in a more practical setting (above is mainly to work with the SELinux policy) I’m all ears.

Posts for Saturday, May 11, 2013

avatar

SELinux policy and network controls

Let’s talk about how SELinux governs network streams (and how it reflects this into the policy).

When you don’t do fancy stuff like SECMARK or netlabeling, then the classes that you should keep an eye on are tcp_socket and udp_socket (depending on the protocol). There used to be node and netif as well, but the support (enforcement) for these have been removed a while ago for the “old style” network control enforcement. The concepts are still available though, and I believe they take effect when netlabeling is used. But let’s first look at the regular networking aspects.

The idea behind the regular network related permissions are that you define either daemon-like behavior (which “binds” to a port) or client-like behavior (which “connects” to a port). Consider an FTP daemon (domain ftpd_t) versus FTP client (example domain ncftp_t).

In case of a daemon, the policy would contain the following (necessary) rules:

corenet_tcp_bind_generic_node(ftpd_t) # Somewhat legacy but still needed
corenet_tcp_bind_ftp_port(ftpd_t)
corenet_tcp_bind_ftp_data_port(ftpd_t)
corenet_tcp_bind_all_unreserved_ports(ftpd_t) # In case of passive mode

This gets translated to the following “real” SELinux statements:

allow ftpd_t node_t:tcp_socket node_bind;
allow ftpd_t ftp_port_t:tcp_socket name_bind;
allow ftpd_t ftp_data_port_t:tcp_socket name_bind;
allow ftpd_t unreserved_port_type:tcp_socket name_bind;

I mention that corenet_tcp_bind_generic_node as being somewhat legacy. When you use netlabeling, you can define different nodes (a “node” in that case is a label assigned to an IP address or IP subnet) and as such define policy-wise where daemons can bind on (or clients can connect to). However, without netlabel, the only node that you get to work with is node_t which represents any possible node. Also, the use of passive mode within the ftp policy is governed through the ftpd_use_passive_mode boolean.

For a client, the following policy line would suffice:

corenet_tcp_connect_ftp_port(ncftp_t)
# allow ncftp_t ftp_port_t:tcp_socket name_connect;

Well, I lied. Because of how FTP works, if you use active connections, you need to allow the client to bind on an unreserved port, and allow the server to connect to unreserved ports (cfr code snippet below), but you get the idea.

corenet_tcp_connect_all_unreserved_ports(ftpd_t)

corenet_tcp_bind_generic_node(ncftp_t)
corenet_tcp_bind_all_unreserved_ports(ncftp_t)

In the past, policy developers also had to include other lines, but these have by time become obsolete (corenet_tcp_sendrecv_ftp_port for instance). These methods defined the ability to send and receive messages on the port, but this is no longer controlled this way. If you need such controls, you will need to look at SELinux and SECMARK (which uses packets with the packet class) or netlabel (which uses the peer class and peer types to send or receive messages from).

And that’ll be for a different post.

Posts for Friday, May 10, 2013

avatar

Gentoo metadata support for CPE

Recently, the metadata.xml file syntax definition (the DTD for those that know a bit of XML) has been updated to support CPE definitions. A CPE (Common Platform Enumeration) is an identifier that describes an application, operating system or hardware device using its vendor, product name, version, update, edition and language. This CPE information is used in the CVE releases (Common Vulnerabilities and Exposures) – announcements about vulnerabilities in applications, operating systems or hardware. Not all security vulnerabilities are assigned a CVE number, but this is as close as you get towards a (public) elaborate dictionary of vulnerabilities.

By allowing Gentoo package maintainers to enter (part of) the CPE information in the metadata.xml file, applications that parse the CVE information can now more easily match if software installed on Gentoo is related to a CVE. I had a related post to this not that long ago on my blog and I’m glad this change has been made. With this information at hand, we can start feeding CPE information to the packages and then easily match this with CVEs.

I had a request to “provide” the scripts I used for the previous post. Mind you, these are taking too many assumptions (and probably wrong ones) for now (and I’m not really planning on updating them as I have different methods for getting information related to CVEs), but I’m planning on integrating CPE data in Gentoo’s packages more and then create a small script that generates a “watchlist” that I can feed to cvechecker. But anyway, here are the scripts.

First, I took all CVE information and put it in a simple CSV file. The CSV is the same one used by cvechecker, so check out the application to see where it fetches the data from (there is a CVE RSS feed and a simple XSL transformation). Second, I create a “hitlist” which generates the CPEs. With the recent change to metadata.xml this step can be simplified a lot. Third, I try to match the CPE data with the CVE data, depending on a given time delay of commits. In other words, you can ask possible CVE fixes for commits made in the last few XXX days.

Posts for Thursday, May 9, 2013

avatar

Enabling Kernel Samepage Merging (KSM)

When using virtualization extensively, you will pretty soon hit the limits of your system (at least, the resources on it). When the virtualization is used primarily for testing (such as in my case), the limit is memory. So it makes sense to seek memory optimization strategies on such systems. The first thing to enable is KSM or Kernel Samepage Merging.

This Linux feature looks for memory pages that the applications have marked as being a possible candidate for optimization (sharing) which are then reused across multiple processes. The idea is that, especially for virtualized environments (but KSM is not limited to that), some processes will have the same contents in memory. Without any sharing abilities, these memory pages will be unique (meaning at different locations in your system’s memory). With KSM, such memory pages are consolidated to a single page which is then referred to by the various processes. When one process wants to modify the page, it is “unshared” so that there is no corruption or unwanted modification of data for the other processes.

Such features are not new – VMWare has it named TPS (Transparent Page Sharing) and Xen calls it “Memory CoW” (Copy-on-Write). One advantage of KSM is that it is simple to setup and advantageous for other processes as well. For instance, if you host multiple instances of the same service (web service, database, tomcat, whatever) there is a high chance that several of its memory pages are prime candidates for sharing.

Now before I do mention that this sharing is only enabled when the application has marked it as such. This is done through the madvise() method, where applications mark the memory with MADV_MERGEABLE, meaning that the applications explicitly need to support KSM in order for it to be successful. There is work on the way to support transparent KSM (such as UKSM and PKSM) where no madvise calls would be needed anymore. But beyond quickly reading the home pages (or translated home pages in case of UKSM ;-) I have no experience with those projects.

So let’s get back to KSM. I am currently running three virtual machines (all configured to take at most 1.5 Gb of memory). Together, they take just a little over 1 Gb of memory (sum of their resident set sizes). When I consult KSM, I get the following information:

 # grep -H '' /sys/kernel/mm/ksm/pages_*
/sys/kernel/mm/ksm/pages_shared:48911
/sys/kernel/mm/ksm/pages_sharing:90090
/sys/kernel/mm/ksm/pages_to_scan:100
/sys/kernel/mm/ksm/pages_unshared:123002
/sys/kernel/mm/ksm/pages_volatile:1035

The pages_shared tells me that 48911 pages are shared (which means about 191 Mb) through 90090 references (pages_sharing – meaning the various processes have in total 90090 references to pages that are being shared). That means a gain of 41179 pages (160 Mb). Note that the resident set sizes do not take into account shared pages, so the sum of the RSS has to be subtracted with this to find the “real” memory consumption. The pages_unshared value tells me that 123002 pages are marked with the MADV_MERGEABLE advise flag but are not used by other processes.

If you want to use KSM yourself, configure your kernel with CONFIG_KSM and start KSM by echo’ing the value “1″ into /sys/kernel/mm/ksm/run. That’s all there is to it.

Posts for Wednesday, May 8, 2013

avatar

The Linux “.d” approach

Many services on a Linux system use a *.d directory approach to make their configuration easily configurable by other services. This is a remarkably simple yet efficient method for exposing services towards other applications. Let’s look into how this .d approach works.

Take a look at the /etc/pam.d structure: services that are PAM-aware can place their PAM configuration files in this location, without needing any additional configuration steps or registration. Same with /etc/cron.d: applications that need specific cronjobs do not need to edit /etc/crontab directly (with the problem of concurrent access, overwriting changes, etc.) but instead can place their definitions in the cron.d directory.

This approach is getting more traction, as can be seen from the available “dot-d” directories on a system:

$ ls -d /etc/*.d
/etc/bash_completion.d  /etc/ld.so.conf.d  /etc/pam.d          /etc/sysctl.d
/etc/conf.d             /etc/local.d       /etc/profile.d      /etc/wgetpaste.d
/etc/dracut.conf.d      /etc/logrotate.d   /etc/request-key.d  /etc/xinetd.d
/etc/env.d              /etc/makedev.d     /etc/sandbox.d      /etc/cron.d
/etc/init.d             /etc/modprobe.d    /etc/sudoers.d

An application can place its configuration files in these directories, automatically “plugging” it in into the operating system and the services that it provides. And the more services adopt this approach, the easier it is for applications to be pluggable within the operating system. Even complex systems such as database systems can easily configure themselves this way. And for larger organizations, this is a very interesting approach.

Consider the need to deploy a database server on a Linux system in a larger organization. Each organization has its standards for file system locations, policies for log file management, etc. With the *.d approach, these organizations only need to put files on the file system (a rather primitive feature that every organization supports) and manage these files instead of using specific, proprietary interfaces to configure the environment. But to properly control this flexibility, a few attention points need to be taken into account.

The first is to use a proper naming convention. If the organization has a data management structure, it might have specific names for services. These names are then used throughout the organization to properly identify owners or responsibilities. When using the *.d directories, these naming conventions also allow administrators to easily know who to contact if a malfunctioning definition is placed. For instance, if a log rotation definition has a wrong entry, a file called mylogrotation does not reveal much information. However, CDBM-postgres-querylogs might reveal that the file is placed there by the customer database management team for a postgresql database. And it isn’t only about knowing who to contact (because that could easily be done by comments as well), but also to ensure no conflicts occur. On a shared database system, it is much more likely that two different teams place a postgresql file (which would overwrite the file already there) unless they use a proper naming convention.

The second is to use something identifying where the file comes from. A best practice when using Puppet for instance is to add in a comment to the file such as the following:

# This file is managed by Puppet through the org-pgsl-def module
# Please do not modify manually

This informs the administrator how the file is put there; you might even want to include version information.

A third one is when the order of configuration entries is important. Most *.d supporting tools do not really care about ordering, but some, like udev, do. When that is the case, the common consensus is to use numbers in the beginning of the file name. The numbers then provide a good ordering of the files.

Not all services already offer *.d functionality, although it isn’t that difficult to provide it as well. Consider the Linux audit daemon, whose rules are managed in the /etc/audit/audit.rules file. Not that flexible, isn’t it? But one can create a /etc/audit/audit.rules.d location and have the audit init script read these files (in alphanumeric order), creating the same functionality.

Given enough service adoption, software distribution can be sufficient to configure an application completely and integrate it with all services used by the operating system. And even services that do not support *.d directories can still be easily wrapped around so that their configuration file itself is generated based on the information in such directories. Consider a hypothetical AIDE configuration, where the aide.conf is generated based on the aide.conf.head, aide.d/* and aide.conf.tail files (similar to how resolv.conf is sometimes managed). The generation is triggered right before aide itself is called (perhaps all in a single script).

Such an approach allows full integration:

  • A PAM configuration file is placed, allowing the service authentication to be easily managed by administrators. Changes on the authentication (for instance, switch to an LDAP authentication or introduce some trust relation) is done by placing an updated file.
  • A log rotation configuration file is placed, making sure that the log files for the service do not eventually fill the partitions
  • A syslog configuration is provided, allowing for some events to be sent to a different server instead of keeping it local – or perhaps both
  • A cron configuration is stored so that statistics and other house-cleaning jobs for the service can run at night
  • An audit configuration snippet is added to ensure critical commands and configuration files are properly checked
  • Intrusion detection rules are added when needed
  • Monitoring information is placed on the file system, causing additional monitoring metrics to be automatically picked up
  • Firewall definitions are extended based on the snippets placed on the system

etc. And all this by only placing files on the file system. Keep It Simple, and efficient ;-)

Posts for Tuesday, May 7, 2013

avatar

Added “predictable network interface” info into the handbook

Being long overdue – like many of our documentation-reported bugs :-( I worked on bug 466262 to update the Gentoo Handbook with information about Network Interface Naming. Of course, the installation instructions have also seen the necessary updates to refer to this change.

With some luck (read: time) I might be able to fix various other documentation-related ones soon. I had some problems with the new SELinux userspace that I wanted to get fixed before, and then I worked on the new SELinux policies as well as trying to figure out how SELinux deals with network related aspects. Hence I saw time fly by at the speed of a neutrino…

BTW, the 20130424 policies are in the tree.

Posts for Monday, May 6, 2013

avatar

Overview of Linux capabilities, part 3

In previous posts I talked about capabilities and gave an introduction to how this powerful security feature within Linux can be used (and also exploited). I also covered a few capabilities, so let’s wrap this up with the remainder of them.

CAP_AUDIT_CONTROL
Enable and disable kernel auditing; change auditing filter rules; retrieve auditing status and filtering rules
CAP_AUDIT_WRITE
Write records to kernel auditing log
CAP_BLOCK_SUSPEND
Employ features that can block system suspend
CAP_MAC_ADMIN
Override Mandatory Access Control (implemented for the SMACK LSM)
CAP_MAC_OVERRIDE
Allow MAC configuration or state changes (implemented for the SMACK LSM)
CAP_NET_ADMIN
Perform various network-related operations:

  • interface configuration
  • administration of IP firewall, masquerading and accounting
  • modify routing tables
  • bind to any address for transparent proxying
  • set type-of-service (TOS)
  • clear driver statistics
  • set promiscuous mode
  • enabling multicasting
  • use setsockopt() for privileged socket operations
CAP_NET_BIND_SERVICE
Bind a socket to Internet domain privileged ports (less than 1024)
CAP_NET_RAW
Use RAW and PACKET sockets, and bind to any address for transparent proxying
CAP_SETPCAP
Allow the process to add any capability from the calling thread’s bounding set to its inheritable set, and drop capabilities from the bounding set (using prctl()) and make changes to the securebits flags.
CAP_SYS_ADMIN
Very powerful capability, includes:

  • Running quota control, mount, swap management, set hostname, …
  • Perform VM86_REQUEST_IRQ vm86 command
  • Perform IPC_SET and IPC_RMID operations on arbitrary System V IPC objects
  • Perform operations on trusted.* and security.* extended attributes
  • Use lookup_dcookie

and many, many more. man capabilities gives a good overview of them.

CAP_SYS_BOOT
Use reboot() and kexec_load()
CAP_SYS_CHROOT
Use chroot()
CAP_SYS_MODULE
Load and unload kernel modules
CAP_SYS_RESOURCE
Another capability with many consequences, including:

  • Use reserved space on ext2 file systems
  • Make ioctl() calls controlling ext3 journaling
  • Override disk quota limits
  • Increase resource limits
  • Override RLIMIT_NPROC resource limits

and many more.

CAP_SYS_TIME
Set system clock and real-time hardware clock
CAP_SYS_TTY_CONFIG
Use vhangup() and employ various privileged ioctl() operations on virtual terminals
CAP_SYSLOG
Perform privileged syslog() operations and view kernel addresses exposed with /proc and other interfaces (if kptr_restrict is set)
CAP_WAKE_ALARM
Trigger something that will wake up the system

Now when you look through the manual page of the capabilities, you’ll notice it talks about securebits as well. This is an additional set of flags that govern how capabilities are used, inherited etc. System administrators don’t set these flags – they are governed by the applications themselves (when creating threads, forking, etc.) These flags are set on a per-thread level, and govern the following behavior:

SECBIT_KEEP_CAPS
Allow a thread with UID 0 to retain its capabilities when it switches its UIDs to a nonzero (non-root) value. By default, this flag is not set, and even if it is set, it is cleared on an execve call, reducing the likelihood that capabilities are “leaked”.
SECBIT_NO_SETUID_FIXUP
When set, the kernel will not adjust the capability sets when the thread’s effective and file system UIDs are switched between zero (root) and non-zero values.
SECBIT_NOROOT
If set, the kernel does not grant capabilities when a setuid-root program is executed, or when a process with an effective or real UID of 0 (root) calls execve.

Manipulating these bits requires the CAP_SETPCAP capability. Except for the SECBIT_KEEP_CAPS security bit, the others are preserved on an execve() call, and all bits are inherited by child processes (such as when fork() is used).

As a user or admin, you can also see capability-related information through the /proc file system:

 # grep ^Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000001fffffffff
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff

$ grep ^Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000001fffffffff

The capabilities listed therein are bitmasks for the various capabilities. The mask 1FFFFFFFFF holds 37 positions, which match the 37 capabilities known (again, see uapi/linux/capabilities.h in the kernel sources to see the values of each of the capabilities). Again, the pscap can be used to get information about the enabled capabilities of running processes in a more human readable format. But another tool provided by the sys-libs/libcap is interested as well to look at: capsh. The tool offers many capability-related features, including decoding the status fields:

$ capsh --decode=0000001fffffffff
0x0000001fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,
cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,
cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,
cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,
cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,
cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,
cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,
cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,
cap_syslog,35,36

Next to fancy decoding, capsh can also launch a shell with reduced capabilities. This makes it a good utility for jailing chroots even more.

Posts for Sunday, May 5, 2013

avatar

Overview of Linux capabilities, part 2

As I’ve (in a very high level) described capabilities and talked a bit on how to work with them, I started with a small overview of file-related capabilities. So next up are process-related capabilities (note, this isn’t a conform terminology, more some categorization that I do myself).

CAP_IPC_LOCK
Allow the process to lock memory
CAP_IPC_OWNER
Bypass the permission checks for operations on System V IPC objects (similar to the CAP_DAC_OVERRIDE for files)
CAP_KILL
Bypass permission checks for sending signals
CAP_SETUID
Allow the process to make arbitrary manipulations of process UIDs and create forged UID when passing socket credentials via UNIX domain sockets
CAP_SETGID
Same, but then for GIDs
CAP_SYS_NICE
This capability governs several permissions/abilities, namely to allow the process to

  • change the nice value of itself and other processes
  • set real-time scheduling priorities for itself, and set scheduling policies and priorities for arbitrary processes
  • set the CPU affinity for arbitrary processes
  • apply migrate_pages to arbitrary processes and allow processes to be migrated to arbitrary nodes
  • apply move_pages to arbitrary processes
  • use the MPOL_MF_MOVE_ALL flag with mbind() and move_pages()

The abilities related to page moving, migration and nodes is of importance for NUMA systems, not something most workstations have or need.

CAP_SYS_PACCT
Use acct(), to enable or disable system resource accounting for the process
CAP_SYS_PTRACE
Allow the process to trace arbitrary processes using ptrace(), apply get_robust_list() against arbitrary processes and inspect processes using kcmp().
CAP_SYS_RAWIO
Allow the process to perform I/O port operations, access /proc/kcore and employ the FIBMAP ioctl() operation.

Capabilities such as CAP_KILL and CAP_SETUID are very important to govern correctly, but this post would be rather dull (given that the definitions of the above capabilities can be found from the manual page) if I wouldn’t talk a bit more about its feasibility. Take a look at the following C application code:

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char ** argv) {
  printf("cap_setuid and cap_setgid: %d\n", prctl(PR_CAPBSET_READ, CAP_SETUID|CAP_SETGID, 0, 0, 0));
  printf(" %s\n", cap_to_text(cap_get_file(argv[0]), NULL));
  printf(" %s\n", cap_to_text(cap_get_proc(), NULL));
  if (setresuid(0, 0, 0));
    printf("setresuid(): %s\n", strerror(errno));
  execve("/bin/sh", NULL, NULL);
}

At first sight, it looks like an application to get root privileges (setresuid()) and then spawn a shell. If that application would be given CAP_SETUID and CAP_SETGID effectively, it would allow anyone who executed it to automatically get a root shell, wouldn’t it?

$ gcc -o test -lcap test.c
# setcap cap_setuid,cap_setgid+ep test
$ ./test
cap_setuid and cap_setgid: 1
 = cap_setgid,cap_setuid+ep
 =
setresuid() failed: Operation not permitted

So what happened? After all, the two capabilities are set with the +ep flags given. Then why aren’t these capabilities enabled? Well, this binary was stored on a file system that is mounted with the nosuid option. As a result, this capability is not enabled and the application didn’t work. If I move the file to another file system that doesn’t have the nosuid option:

$ /usr/local/bin/test
cap_setuid and cap_setgid: 1
 = cap_setgid,cap_setuid+ep
 = cap_setgid,cap_setuid+ep
setresuid() failed: Operation not permitted

So the capabilities now do get enabled, so why does this still fail? This now is due to SELinux:

type=AVC msg=audit(1367393377.342:4778): avc:  denied  { setuid } for  pid=21418 comm="test" capability=7  scontext=staff_u:staff_r:staff_t tcontext=staff_u:staff_r:staff_t tclass=capability

And if you enable grSecurity’s TPE, we can’t even start the binary to begin with:

$ ./test
-bash: ./test: Permission denied
$ /lib/ld-linux-x86-64.so.2 /home/test/test
/home/test/test: error while loading shared libraries: /home/test/test: failed to map segment from shared object: Permission denied

# dmesg
...
[ 5579.567842] grsec: From 192.168.100.1: denied untrusted exec (due to not being in trusted group and file in non-root-owned directory) of /home/test/test by /home/test/test[bash:4221] uid/euid:1002/1002 gid/egid:100/100, parent /bin/bash[bash:4195] uid/euid:1002/1002 gid/egid:100/100

When all these “security obstacles” are not enabled, then the call succeeds:

$ /usr/local/bin/test
cap_setuid and cap_setgid: 1
 = cap_setgid,cap_setuid+ep
 = cap_setgid,cap_setuid+ep
setresuid() failed: Success
root@hpl tmp # 

This again shows how important it is to regularly review capability-enabled files on the file system, as this is a major security problem that cannot be detected by only looking for setuid binaries, but also that securing a system is not limited to one or a few settings: one always has to take the entire setup into consideration, hardening the system so it becomes more difficult for malicious users to abuse it.

# filecap -a
file                 capabilities
/usr/local/bin/test     setgid, setuid

Posts for Saturday, May 4, 2013

avatar

Overview of Linux capabilities, part 1

In the previous posts, I talked about capabilities and how they can be used to allow processes to run in a privileged fashion without granting them full root access to the system. An example given was how capabilities can be leveraged to run ping without granting it setuid root rights. But what are the various capabilities that Linux is, well, capable of?

There are many, and as time goes by, more capabilities are added to the set. The last capability added to the main Linux kernel tree was the CAP_BLOCK_SUSPEND in the 3.5 series. An overview of all capabilities can be seen with man capabilities or by looking at the Linux kernel source code, include/uapi/linux/capability.h. But because you are all lazy, and because it is a good exercise for myself, I’ll go through many of them in this and the next few posts.

For now, let’s look at file related capabilities. As a reminder, if you want to know which SELinux domains are “granted” a particular capability, you can look this up using sesearch. The capability is either in the capability or capability2 class, and is named after the capability itself, without the CAP_ prefix:

$ sesearch -c capability -p chown -A
CAP_CHOWN
Allow making changes to the file UIDs and GIDs.
CAP_DAC_OVERRIDE
Bypass file read, write and execute permission checks. I came across a reddit post that was about this capability not that long ago.
CAP_DAC_READ_SEARCH
Bypass file read permission and directory read/search permission checks.
CAP_FOWNER
This capability governs 5 capabilities in one:

  • Bypass permission checks on operations that normally require the file system UID of the process to match the UID of the file (unless already granted through CAP_DAC_READ_SEARCH and/or CAP_DAC_OVERRIDE)
  • Allow to set extended file attributes
  • Allow to set access control lists
  • Ignore directory sticky bit on file deletion
  • Allow specifying O_NOATIME for files in open() and fnctl() calls
CAP_FSETID
Do not clear the setuid/setgid permission bits when a file is modified
CAP_LEASE
Allow establishing leases on files
CAP_LINUX_IMMUTABLE
Allow setting FS_APPEND_FL and FP_IMMUTABLE_FL inode flags
CAP_MKNOD
Allow creating special files with mknod
CAP_SETFCAP
Allow setting file capabilities (what I did with the anotherping binary in the previous post)

When working with SELinux (especially when writing applications), you’ll find that the CAP_DAC_READ_SEARCH and CAP_DAC_OVERRIDE capability come up often. This is the case when applications are written to run as root yet want to scan through, read or even execute non-root owned files. Without SELinux, because these run as root, this is all granted. However, when you start confining those applications, it becomes apparent that they require this capability. Another example is when you run user applications, as root, like when trying to play a movie or music file with mplayer when this file is owned by a regular user:

type=AVC msg=audit(1367145131.860:18785): avc:  denied  { dac_read_search } for
pid=8153 comm="mplayer" capability=2  scontext=staff_u:sysadm_r:mplayer_t
tcontext=staff_u:sysadm_r:mplayer_t tclass=capability

type=AVC msg=audit(1367145131.860:18785): avc:  denied  { dac_override } for
pid=8153 comm="mplayer" capability=1  scontext=staff_u:sysadm_r:mplayer_t
tcontext=staff_u:sysadm_r:mplayer_t tclass=capability

Notice the time stamp: both checks are triggered at the same time. What happens is that the Linux security hooks first check for DAC_READ_SEARCH (the “lesser” grants of the two) and then for DAC_OVERRIDE (which contains DAC_READ_SEARCH and more). In both cases, the check failed in the above example.

The CAP_LEASE capability is one that I had not heard about before (actually, I had not heard of getting “file leases” on Linux either). A file lease allows for the lease holder (which requires this capability) to be notified when another process tries to open or truncate the file. When that happens, the call itself is blocked and the lease holder is notified (usually using SIGIO) about the access. It is not really to lock a file (since, if the lease holder doesn’t properly release it, it is forcefully “broken” and the other process can continue its work) but rather to properly close the file descriptor or flushing caches, etc.

BTW, on my system, only 5 SELinux domains hold the lease capability.

There are 37 capabilities known by the Linux kernel at this time. The above list has 9 file related ones. So perhaps next I can talk about process capabilities.

Planet Larry is not officially affiliated with Gentoo Linux. Original artwork and logos copyright Gentoo Foundation. Yadda, yadda, yadda.