zaterdag 1 december 2012

Anatomy of a SELinux policy module: Dropbox

I was asked to write a SELinux policy module for Dropbox. For obvious reasons i do not use this user application myself but i was willing to make an exception for the sake of writing a SELinux policy configuration for Dropbox.

The module was designed for and developed on a Fedora 18 (Beta) system.

Before i start designing and writing a policy configuration for some application i usually do my home work first. That means reading up on the application as much as i can so that i have an idea what to expect.

In this case i already had a rough idea about the applications functionality. I was interested in seeing what is installed where and when.

And so i downloaded and extracted two packages from this location:

https://www.dropbox.com/install

I focussed on this package:

https://dl-web.dropbox.com/u/17/dropbox-lnx.x86_64-1.6.2.tar.gz

and also had a quick look at this:

https://www.dropbox.com/download?dl=packages/fedora/nautilus-dropbox-1.4.0-1.fedora.x86_64.rpm

Basically i downloaded both packages and extracted it to see what was in them.

The dropbox-lnx.x86_64-1.6.2.tar.gz package is expected to be downloaded to "~"  and to be extracted there. It extracts a directory called ".dropbox-dist" with various files in it.

The installation guide suggests that one runs the "dropboxd" file that is location in the extracted "~/.dropbox-dist"  location.

And so i decided to just try it out.

As it turns out the dropboxd programs runs another file in that location called "dropbox". Another file in that directory called "library.zip" turned out to be a "hard link" to " dropbox". I was also able to identify plenty library files by their ".so" extensions.

Anyways; A graphical window popped up and presented me with a series of questions. I followed the directions and when i was done the window exited and the process installed two more directories in my home directories called "~/.dropbox" and "~/Dropbox"  respectively.

The " ~/.dropbox"  directory has various files that seem to be configuration files of some sorts and the "~/Dropbox" directory is the location that gets synchronized.

Now i' ve had a rough idea of the locations and the nature of the files that were installed.

The next step was to think about design.

Were using the common security models available to us: Role based access control and Type enforcement. The aim is to improve integrity of our processes and files.

We must also do our best to support as much of the applications functionality as we possibly can.

We need to maintain a sub-tile balance between usability and security.

Designing policy configuration for applications that sit on top of a Desktop environment is more complicated than a text application that you can for example run from a console because they usually interact with components on the Desktop environment and operate on files maintained by the these components.

In a stock Fedora SELinux policy configuration most of the Desktop enviroment is not targeted. The result of this design decision is that components of the Desktop enviroment run with the same attributes and permissions as the user running them.

For us that means that we either target each Desktop environment component and each file they maintain that Dropbox interacts with, or we allow our targeted Dropbox application to interact with the components operating, and operate on files they maintain, with the attributes and permissions of the user.

I decided to go with the latter for simplicity.

With that in mind i set out some humble security goals to achieve:

1. Protect user application configuration, data and cache files as much as possible.
2. Protect and contain the Dropbox application and process as much as possible.

What follows now is my current SELinux policy configuration for Dropbox. The nautilus plugin (nautilus-dropbox-1.4.0-1.fedora.x86_64.rpm) is NOT supported. At least not currently.

This because i refuse to install it because it depends on libgnome which in turn depends on Orbit and Bonobo and i will do just about anything to not have that installed.

Here is the configuration. Below the configuration i will touch on its properties in detail:

1. The ~/mydopbox/mydropbox.te Type enforcement source policy file:

policy_module(mydropbox, 1.0.0)

attribute dropbox_domain;

type dropbox_exec_t;

type dropbox_home_t;
userdom_user_home_content(dropbox_home_t)

type dropbox_tmp_t;
userdom_user_tmp_content(dropbox_tmp_t)

type dropbox_tmpfs_t;
userdom_user_tmpfs_content(dropbox_tmpfs_t)

type dropbox_port_t;
corenet_port(dropbox_port_t)

allow dropbox_domain self:capability dac_override; # mount
allow dropbox_domain self:netlink_route_socket r_netlink_socket_perms;
allow dropbox_domain self:process { execmem signal };
allow dropbox_domain self:shm create_shm_perms;
allow dropbox_domain self:tcp_socket create_stream_socket_perms;
allow dropbox_domain self:udp_socket create_socket_perms;

allow dropbox_domain dropbox_home_t:dir manage_dir_perms;
allow dropbox_domain dropbox_home_t:file manage_file_perms;
allow dropbox_domain dropbox_home_t:sock_file manage_sock_file_perms;
userdom_user_home_dir_filetrans(dropbox_domain, dropbox_home_t, dir, ".dropbox")

allow dropbox_domain dropbox_tmp_t:file { manage_file_perms mmap_file_perms };
files_tmp_filetrans(dropbox_domain, dropbox_tmp_t, file)

can_exec(dropbox_domain, dropbox_exec_t)

kernel_getattr_core_if(dropbox_domain)

corecmd_exec_shell(dropbox_domain)

corenet_tcp_bind_generic_node(dropbox_domain)
corenet_tcp_sendrecv_generic_if(dropbox_domain)
corenet_tcp_sendrecv_generic_node(dropbox_domain)
corenet_udp_bind_generic_node(dropbox_domain)
corenet_udp_sendrecv_generic_if(dropbox_domain)
corenet_udp_sendrecv_generic_node(dropbox_domain)

corenet_sendrecv_http_client_packets(dropbox_domain)
corenet_tcp_connect_http_port(dropbox_domain)
corenet_tcp_sendrecv_http_port(dropbox_domain)

allow dropbox_domain dropbox_port_t:{ tcp_socket udp_socket } name_bind; # temporary workaround: 17500

dev_list_sysfs(dropbox_domain)
dev_read_sysfs(dropbox_domain)
dev_read_urand(dropbox_domain)

dev_dontaudit_getattr_all_blk_files(dropbox_domain) # panic
dev_dontaudit_getattr_all_chr_files(dropbox_domain) # panic

fs_getattr_tmpfs(dropbox_domain)
fs_getattr_xattr_fs(dropbox_domain)
fs_rw_inherited_tmpfs_files(dropbox_domain) # this is that xserver shm thing

auth_read_passwd(dropbox_domain)

init_getattr_initctl(dropbox_domain)

libs_exec_ldconfig(dropbox_domain)

mount_exec(dropbox_domain)
mount_manage_pid_files(dropbox_domain) # mount: read/write /run/mount/utab

sysnet_exec_ifconfig(dropbox_domain)
sysnet_read_config(dropbox_domain)

userdom_manage_user_home_content_dirs(dropbox_domain)
userdom_manage_user_home_content_files(dropbox_domain)
userdom_mmap_user_home_content_files(dropbox_domain) # libraries in ~/.dropbox-dist
userdom_user_home_dir_filetrans_user_home_content(dropbox_domain, dir) # cannot use named file transition due to random names
userdom_use_user_terminals(dropbox_domain)

optional_policy(`
 dbus_session_bus_client(dropbox_domain) # probably not actually optional
 dbus_connect_session_bus(dropbox_domain) # probably not actually optional
')

optional_policy(`
 gnome_read_generic_data_home_dirs(dropbox_domain) # searching ~/.local/share
 gnome_read_home_config(dropbox_domain) # ibus, might not be optional

 # hack
 gen_require(`
  type config_home_t;
 ')

 allow dropbox_domain config_home_t:dir setattr_dir_perms;
')
 2. The ~/mydopbox/mydropbox.if Interface source policy file:


Blogspot does not format this content properly: View/Get it here instead:

http://pastebin.com/zcfSK2n3


## Dropbox is a free service that lets you bring all your photos, docs, and videos anywhere.

#######################################
## 
## The role template for the dropbox module.
## 
## 
## 
## This template creates a derived domains which are used
## for dropbox applications.
## 


## 
## 
## 
## The prefix of the user domain (e.g., user
## is the prefix for user_t).
## 
## 
## 
## 
## The role associated with the user domain.
## 
## 
## 
## 
## The type of the user domain.
## 
## 
#
template(`dropbox_role_template',`
 gen_require(`
  attribute dropbox_domain;
  type dropbox_exec_t, dropbox_home_t, dropbox_tmpfs_t;
 ')

 ########################################
 #
 # Declarations
 #

 type $1_dropbox_t, dropbox_domain;
 userdom_user_application_domain($1_dropbox_t, dropbox_exec_t)
 role $2 types $1_dropbox_t;

 ########################################
 #
 # Policy
 #

 domtrans_pattern($3, dropbox_exec_t, $1_dropbox_t)

 ps_process_pattern($3, $1_dropbox_t)
 allow $3 $1_dropbox_t:process { ptrace signal_perms };

 allow $1_dropbox_t $3:process signull;
 allow $1_dropbox_t $3:unix_stream_socket connectto;

 allow $3 dropbox_exec_t:file { manage_file_perms relabel_file_perms };
 userdom_user_home_content_filetrans($3, dropbox_exec_t, file, "dropbox")
 userdom_user_home_content_filetrans($3, dropbox_exec_t, file, "dropboxd")
 userdom_user_home_content_filetrans($3, dropbox_exec_t, file, "library.zip")

 allow $3 dropbox_home_t:dir { manage_dir_perms relabel_dir_perms };
 allow $3 dropbox_home_t:file { manage_file_perms relabel_file_perms };
 allow $3 dropbox_home_t:sock_file { manage_sock_file_perms relabel_sock_file_perms };
 userdom_user_home_dir_filetrans($3, dropbox_home_t, dir, ".dropbox")

 kernel_read_system_state($1_dropbox_t)

 corecmd_bin_domtrans($1_dropbox_t, $3)

 corenet_all_recvfrom_unlabeled($1_dropbox_t)
 corenet_all_recvfrom_netlabel($1_dropbox_t)

 logging_send_syslog_msg($1_dropbox_t) # might want to make this conditional if possible

 optional_policy(`
  dropbox_dbus_chat($1, $3) # probably not actually optional
 ')

 optional_policy(`
  xserver_user_x_domain_template($1_dropbox, $1_dropbox_t, dropbox_tmpfs_t) # might not be optional
 ')
')

########################################
## 
## Send and receive messages from
## dropbox over dbus.
## 
## 
## 
## The prefix of the user domain (e.g., user
## is the prefix for user_t).
## 
## 
## 
## 
## Domain allowed access.
## 
## 
#
interface(`dropbox_dbus_chat',`
 gen_require(`
  type $1_dropbox_t;
  class dbus send_msg;
 ')

 allow $2 $1_dropbox_t:dbus send_msg;
 allow $1_dropbox_t $2:dbus send_msg;
')

 
 3. The ~/mydopbox/mydropbox.fc File contexts source policy file:


HOME_DIR/\.dropbox(/.*)? gen_context(system_u:object_r:dropbox_home_t,s0)
HOME_DIR/\.dropbox-dist/dropbox(d)? -- gen_context(system_u:object_r:dropbox_exec_t,s0)
HOME_DIR/\.dropbox-dist/library\.zip -- gen_context(system_u:object_r:dropbox_exec_t,s0)
Declaring types, classifying them and making sure that SELinux knows what to label what

Note that the policy above is the current result. Many of the properties of Dropbox where discovered after studying the contents of the package by trial and error.

type dropbox_exec_t;
This is the declaration of the type of the dropbox executable files. Since "library.zip" is a hard link of "dropbox", we need to make sure that both are labeled identical. The file "dropboxd" is also a Dropbox user application executable file.

HOME_DIR/\.dropbox-dist/dropbox(d)? -- gen_context(system_u:object_r:dropbox_exec_t,s0)
HOME_DIR/\.dropbox-dist/library\.zip -- gen_context(system_u:object_r:dropbox_exec_t,s0)
Above is how we instruct SELinux about where these Dropbox application executable files are located and how they should be labeled.


type dropbox_home_t;
userdom_user_home_content(dropbox_home_t)
This is the declaration of the Dropbox user configuration content located in "~/.dropbox" as well as the interface call that classifies the type as " user home content".

HOME_DIR/\.dropbox(/.*)? gen_context(system_u:object_r:dropbox_home_t,s0)
Here we tell SELinux that the ~/.dropbox directory and everything in it should be labeled with type "dropbox_home_t" which is the Dropbox user home content type.

type dropbox_tmp_t;
userdom_user_tmp_content(dropbox_tmp_t)
Dropbox maintains a file in " $TMP". Here we declare a type "dropbox_tmp_t" for this file and we classify this file "user tmp content" by calling the "userdom_user_tmp_content() interface with the "dropbox_tmp_t" file type parameter.

SELinux does not have to be aware of the location of the file in "$TMP" since for reason that i will not touch on in this article it should not maintain any contexts in "$TMP". Hence there is no entry for this in the "~/mydropbox/mydropbox.fc"  file.

type dropbox_tmpfs_t;
userdom_user_tmpfs_content(dropbox_tmpfs_t)
Dropbox opens a GTK window when you first run it to guide you through the installation process. Dropbox also has i icon in the taskbar that opens a settings window if you select it. The result is that Dropbox interacts with X server and operates on content maintained by X server.

For this we have to declare a type for Dropbox shared memory in "/dev/shm". We classify this type "user tmpfs content".

There is no need to specificy a file context for this content as SELinux should not maintain file contexts in this locations for the same reasons as it should not maintain them in "$TMP".


type dropbox_port_t;
corenet_port(dropbox_port_t)
Dropbox listens on the UDP and TCP network for connections on port 17500. We Declare a type for this port object and classify the type "corenet_port". This will allow us to tell SELinux that Dropbox may only bind TCP and UDP sockets to ports that are classified "dropbox_port_t".

Ports are not files and thus their contexts should not be specified in the file context file (~/mydropbox/mydropbox.fc).

Instead we need to , after we installed the policy module package, manually label the 17500 UDP and TCP ports type "dropbox_port_t"

This is done by issuing the following command:

semanage port -a -t dropbox_port_t -p tcp 17500
semanage port -a -t dropbox_port_t -p udp 17500
You may have noticed that we have not yet classified our " dropbox_exec_t" user application executable file type.

You may also have notices that we have not yet declared and classified a type for the Dropbox process.

This is because of the properties of this application and it is also related to my design decision.

Dropbox runs Nautilus and Firefox on your behalf to open your "~/Dropbox"  location and to direct you to the "www.dropbox.com"  website respectively.

These two applications are currently not targeted in Fedora and i have decided to not do that either.

How can we tell SELinux that if Dropbox runs Nautilus or Firefox that it does that on behalf of the user and that SELinux thus should run these two applications with the attributes and permissions of the user rather than the attributes and permission of Dropbox?

This requires that we create a template because not all SELinux users are created equal. We need to use the "user role prefix" to declare a derived Dropbox process type. This will allow use to create rules that specify with which attributes and permissions Dropbox should run Nautilus and Firefox.

This is done in a template called "dropbox_role_template" that i have created in the "~/mydropbox/mydropbox.if"  interface file.

 type $1_dropbox_t, dropbox_domain;
 userdom_user_application_domain($1_dropbox_t, dropbox_exec_t)
 role $2 types $1_dropbox_t;
 
The above declares a derived dropbox process type "$1_dropbox_t" where "$1"  is the "user role prefix". It then goes on to classify this process "dropbox_domain"  This is done by assigning it a "type attribute"  that we i declared in the "~/mydropbox/mydropbox.te"  type enforcement file. You can find it on top, right below the policy module declaration.

Next we classify both the Dropbox process type as well as the Dropbox user application executable file type "user application domain". This interface has all the permissions needed to classify our process type "user application process" and our application executable file "user application executable file".

The last rule is a "Role based access control"  rule that allows the "calling" role access to the prefixed Dropbox type.

 Policy.

domtrans_pattern($3, dropbox_exec_t, $1_dropbox_t)

This rule is a domain transition rule, or if you like "process type transition rule". It tells SELinux that the "calling" user process type should transition to the derived Dropbox process type when it runs the Dropbox user application executable file.


 ps_process_pattern($3, $1_dropbox_t)
 allow $3 $1_dropbox_t:process { ptrace signal_perms };

The above rules allow the calling user process type to "ps" processes labeled with the derived Dropbox process type as well as ptrace it and send all signals to it.
logging_send_syslog_msg($1_dropbox_t) 

This will allow Dropbox to for example show up in "top", " ps x"  and it allows you to "strace", kill etc. the Dropbox process.

 allow $1_dropbox_t $3:process signull;
 allow $1_dropbox_t $3:unix_stream_socket connectto;

Here are some interesting rules. The first allows the derived Dropbox process type to send null signals to the calling user process type and the second one allows the derived Dropbox process type to connect to the calling user process type with a unix domain stream socket.

The processes here labeled with the "calling user process types"  aren' t actually the calling user. These are for example Nautilus or Firefox. Since we decided not to target them they are labeled with the " calling user process type".

In this case Dropbox probably wants to see if Nautilus is already running, and Dropbox probably wants to communicate with Nautilus using a unix domain stream socket.

These rules are problematic because they are coarse. You can't tell by just looking at these rules who "$3" or if you will the process labeled with the "calling user process type" really is. Is it Nautilus or is it Firefox? Maybe it another process that currently is not targeted.

Basically it allows Dropbox to send null signals and talk using a unix domain stream socket to any application currently not targeted labeled with the "calling user process type".

 allow $3 dropbox_exec_t:file { manage_file_perms relabel_file_perms };
 userdom_user_home_content_filetrans($3, dropbox_exec_t, file, "dropbox")
 userdom_user_home_content_filetrans($3, dropbox_exec_t, file, "dropboxd")
 userdom_user_home_content_filetrans($3, dropbox_exec_t, file, "library.zip")

These rules allow processes labeled with the " calling user process type" to "manage and relabel"  files labeled with the dropbox use application executable type " dropbox_exec_t".

Users need to be able to manage and relabel content in their home directory as they own it.

The other three rules are interesting as well. They use a pretty new feature called "named file type transitions",

These rules tell SELinux that if a process labeled with the " calling user process type" create files with named "dropbox, dropboxd and library.zip"  in directories that are labeled with the user home content type "user_home_t" that the files should be created with the dropbox user application executable file type "dropbox_exec_t".

This is a important rule because proper labelling of files is of vital importance to the integrity of SELinux policy enforcement.

When you extract the  "dropbox-lnx.x86_64-1.6.2.tar.gz"  into you home directory, this rule ensure that the Dropbox executable files automatically get the right security context.

The file context specification for these files that we added to " ~/mydropbox/mydropbox.fc"  ensure that if restorecon gets run on any and all of these files that they will stay labeled properly.


 allow $3 dropbox_home_t:dir { manage_dir_perms relabel_dir_perms };
 allow $3 dropbox_home_t:file { manage_file_perms relabel_file_perms };
 allow $3 dropbox_home_t:sock_file { manage_sock_file_perms relabel_sock_file_perms };
 userdom_user_home_dir_filetrans($3, dropbox_home_t, dir, ".dropbox") 

The same applies to the above except that this applies to Dropbox user home content and it applies to files, directories and sock files.

The named file transtion rule dictates that SELinux should make sure that processes with the calling user process type create directories with the name ".dropbox" in directories with type "user_home_dir_t" (~) with type "dropbox_home_t".

Note: I should probably allow allow processes with the calling process type to manage and relabel files with type dropbox_tmp_t as well dropbox_tmpfs_t.


 kernel_read_system_state($1_dropbox_t)

This rule allows the derived Dropbox process type to read generic system state files in "/proc", like for example "/proc/meminfo".

We added this rule (and some other rules that would normally go into the type enforcement file) here because it uses type attributes and were also using our "dropbox_domain" type attribute in our type enforcement file to write rules.

One can only assign type attribute to types. You cannot assign a type attribute to a type attribute and thus we decided to deal with this in this manner.

corecmd_bin_domtrans($1_dropbox_t, $3)

This is the rule that caused me to implement the "dropbox_role_template" template.

It is what enables us to tell SELinux that dropbox should run application that are not targeted with the "calling user process type".

Basically it dictates that when the derived Dropbox process type runs a file with the generic "bin_t"  "core command executable type" that the process type should transition from "$1_dropbox_t" (the derived Dropbox process type) to "$3" ( the calling user process type)

 corenet_all_recvfrom_unlabeled($1_dropbox_t)
 corenet_all_recvfrom_netlabel($1_dropbox_t)

These rules allow the derived Dropbox process type to recv from all unlabeled and netlabel network connections.

It is basically a rule that effectivily disabled SELinux network controls enforcement.

These rules are here because again they use type attributes and one cannot assign type attributes to type attributes. That means we cannot use our "dropbox_domain"  attribute to call these in our ~/mydropbox/mydropbox.te" file.


logging_send_syslog_msg($1_dropbox_t) 

This rule allows the prefixed Dropbox process type to send messages to syslog. Same as above it uses type attributes and so i had to add it here instead of in the type enforcement file.


 optional_policy(`
  dropbox_dbus_chat($1, $3) # probably not actually optional
 ')
This allows the derived dropbox process type to communicate using DBUS with processes that are labeled with the "calling user process type". Probably Nautilus or some Gnome Desktop environment compoment like GVFS.

Since None of them are targeted we are forced to allow the prefixed dropbox process type to communicate using DBUS with any process as long as its labeled with the "calling user process type".

We could have used raw policy to write these rules here but since DBUS is a "object manager" and also since its good to have a " dropbox_dbus_chat" interface we decide to create one (you can find it below the "dropbox_role_template" template in "~/mydropbox/mydropbox.if" and to call it in our own module.

The "optional_policy" block indicates that this module does not actually depend on the rule in the block. I was assuming that Dropbox does not depend on the presence of DBUS but i may well be mistaken.

 optional_policy(`
  xserver_user_x_domain_template($1_dropbox, $1_dropbox_t, dropbox_tmpfs_t) # might not be optional
 ')

This rule allows the derived Dropbox process type to interact with X server and to operate on files maintained by X server. It also makes our "dropbox_tmpfs_t" "user tmpfs content" available for use with X server.

I was under the impression that Dropbox does not depend on X server since on their website they claim that Dropbox works on "any Linux server" but again i may be mistaken.

allow dropbox_domain self:capability dac_override; # mount
allow dropbox_domain self:netlink_route_socket r_netlink_socket_perms;
allow dropbox_domain self:process { execmem signal };
allow dropbox_domain self:shm create_shm_perms;
allow dropbox_domain self:tcp_socket create_stream_socket_perms;
allow dropbox_domain self:udp_socket create_socket_perms;

These are what i call " self-rules". Basically rules where a source process labeled with a process type interacts with or operates on itself.

The first rule allow any Dropbox process type the "dac_override"  capability. This capability is needed to override discretionary access controls.

Dropbox runs the mount command with the derived Dropbox process type (rather than with a process type transition) which is a setuid command.

When the command is run your shell sessions current "pwd" is probably "~/.dropbox-dist"  since thats where you run dropboxd from. The root Linux user does (or might not) have access to that location as " $HOME"  might have the "0700" attibutes. The "dac_override"  access vector permission allow the process type to access it anyway from a SELinux perspective.

The second rule is probably to read the routing table. Dropbox is a network application and so i am not surprised that it needs these permissions.

The third rule allows all Dropbox process types to "execute anonymous memory", Basically memory that is world writable. It is usually a sign of bad programming practices but there are also legitimate use cases such as "just in time compiling". This rule also allows Dropbox process types to send generic signals to itself.

The fourth rule allows Dropbox processes to create shared memory. This is for X server interaction and is related to the " dropbox_tmpfs_t"  "user_tmpfs_content"  type.

The fifth and the sixth rule allows dropbox and any other process labeled with any dropbox process type to create tcp stream sockets and udp sockets. This is needed because Dropbox is listening on the network for connections (port udp/tcp 17500 dropbox_port_t)

allow dropbox_domain dropbox_home_t:dir manage_dir_perms;
allow dropbox_domain dropbox_home_t:file manage_file_perms;
allow dropbox_domain dropbox_home_t:sock_file manage_sock_file_perms;
userdom_user_home_dir_filetrans(dropbox_domain, dropbox_home_t, dir, ".dropbox")

These rules allow all Dropbox process types to "manage"  directories, files and sock files that are labeled with the "dropbox_home_t" " user home content type". This is configuration content amongst other things. Stuff that could use protecting to ensure optimal integrity.

The fourth rule is another named file type transition rule. It dictates that if any dropbox process type creates a directory with name ".dropbox"  in directories with type " user_home_dir_t"  which is "~"  that the directory be created with the "dropbox_home_t" type. Thus file transitioning from "user_home_dir_t" to "dropbox_home_t".


allow dropbox_domain dropbox_tmp_t:file { manage_file_perms mmap_file_perms };
files_tmp_filetrans(dropbox_domain, dropbox_tmp_t, file)

Dropbox maintains a file in "$TMP" with a random name. It also "mmaps" that file. The second rule dictates that if any Dropbox process type creates a file in a directory with type " tmp_t" that the file is created with the " dropbox_tmp_t"  " user_tmp_content" type. Since this file has a random name i have to use a regular file type transition and i cannot use a named file type transition.

can_exec(dropbox_domain, dropbox_exec_t)

The dropboxd process executes the dropbox "user application executable file"  Both dropboxd as well as dropbox are labeled with the " dropbox_exec_t"  type. This rule allows all Dropbox process types to execute files with the dropbox_exec_t type without a process type transition.


kernel_getattr_core_if(dropbox_domain)

I am not sure why but Dropbox wants to get attribute of the core if file in "/proc". Fine.


corecmd_exec_shell(dropbox_domain)

This rule allows all Dropbox process types to run a shell without a process type transition.


corenet_tcp_bind_generic_node(dropbox_domain)
corenet_tcp_sendrecv_generic_if(dropbox_domain)
corenet_tcp_sendrecv_generic_node(dropbox_domain)
corenet_udp_bind_generic_node(dropbox_domain)
corenet_udp_sendrecv_generic_if(dropbox_domain)
corenet_udp_sendrecv_generic_node(dropbox_domain)

corenet_sendrecv_http_client_packets(dropbox_domain)
corenet_tcp_connect_http_port(dropbox_domain)
corenet_tcp_sendrecv_http_port(dropbox_domain)

allow dropbox_domain dropbox_port_t:{ tcp_socket udp_socket } name_bind; # temporary workaround: 17500

Network related rules. The first block of rules are "generic" in the sense that it basically does not support the use of the SELinux network controls. Or it supports them in the most generic way.

The second block allows all dropbox process types to connect to TCP ports that are labeled with the "httpd_port_t"  port type. Dropbox connects to "tcp:443" (their Dropbox web application). It allow Dropbox to send and receive client packets that are labeled with the httpd packet type and it allows Dropbox to send and receive traffic from http classified ports.

The last rule of the bunch allows all dropbox process types to bind TCP and UDP sockets to ports that are labeled with the "dropbox_port_t"  port type. We labeled UDP/TCP 17500 with that type.

dev_list_sysfs(dropbox_domain)
dev_read_sysfs(dropbox_domain)
dev_read_urand(dropbox_domain)

Above allows all Dropbox process types to list directories with the generic "sysfs_t" type. This is generic content in "/sys". The second rule allow Dropbox to actually read files with this type.

The third rule allows all Dropbox process types to read character device nodes with "urandom_device_t" device node type. E.g. /dev/urandom.

dev_dontaudit_getattr_all_blk_files(dropbox_domain) # panic
dev_dontaudit_getattr_all_chr_files(dropbox_domain) # panic

"Dontaudit" is a access vector that tells SELinux to silently block a specified interaction or operation.

In the above case we silently deny any Dropbox process type to get attributes of all block and character files that are classified "device_node".

To expose " hidden denials" one needs to issue the "semodule -DB"  command which will build the policy database without any " dontaudit" rules. To reinsert the "dontaudit" rules simply issue 'semodule -B".

fs_getattr_tmpfs(dropbox_domain)
fs_getattr_xattr_fs(dropbox_domain)
fs_rw_inherited_tmpfs_files(dropbox_domain) # this is that xserver shm thing 

This allow all Dropbox process types to get attributes of filesystems with the "tmpfs_t"  filesystem type. This a common access vector for processes that maintain content in the " /dev/shm"  location.

The second rule allow any Dropbox process type to get attribute of any filesystem that supports extended attributes. E.g. types that are classified "filesystem type" as well as "Extended attribute file system". This is to allow Dropbox to stat "/"  which is a common thing to do.

The Third rule is related to X server interaction. I am not sure why it is the way it is but it has been this way for as long as i can remember. Inherited means that it doesnt actually opens the "generic tmpfs" file but it still reads and writes it. That means that either Dropbox got passed a open file or that there is a leaked file descriptor. Since, if i remember correctly , things will break if you do not allow this, i assume that X server or whatever passed the open file to Dropbox and dropbox reads and write it.

Note that the file is not labeled with the "dropbox_tmpfs_t"  "user tmpfs content" file type either. Instead it is labeled with a generic type for content in "/dev/shm".


auth_read_passwd(dropbox_domain)

Allow all Dropbox process types to read files with the passwd_file_t file type. E.g. "/etc/passwd" amongst others.

This could simply be a side effect from Dropbox running a bash shell. The shell needs to read the password file in order to get the information needed for the shell prompt (user name etc.)


init_getattr_initctl(dropbox_domain)

Get attributes of initctl (named pipe i believe it was)

libs_exec_ldconfig(dropbox_domain)

Execute ldconfig program without a process type transition. It probably runs ldconfig on that file it maintains in "$TMP"  that it also mmaps.

mount_exec(dropbox_domain)
mount_manage_pid_files(dropbox_domain) # mount: read/write /run/mount/utab
Execute the mount command without a process type transition. Not sure what it does with mount.

Mount reads and write /run/mount/utab which is labeled with the mount pid file type.

sysnet_exec_ifconfig(dropbox_domain)
sysnet_read_config(dropbox_domain)

Executes ifconfig command without a process type transition and read files that are classified network configuration files (net_conf_t) This is probably "/etc/resolve.conf"

userdom_manage_user_home_content_dirs(dropbox_domain)
userdom_manage_user_home_content_files(dropbox_domain)
userdom_mmap_user_home_content_files(dropbox_domain) # libraries in ~/.dropbox-dist
userdom_user_home_dir_filetrans_user_home_content(dropbox_domain, dir) # cannot use named file transition due to random names
userdom_use_user_terminals(dropbox_domain)

These are interesting. These rules allow all dropbox process types to operate on content maintained by processes labeled with the user process type.

In some cases this is bad news but in our case we will have to embrace it.

Here is why

The first two rules allow all Dropbox process types to manage directories and files that are labeled with the generic user home content type (user_home_t)

I decided to keep "~/Dropbox" location generic. The idea of Dropbox is that users can "drag and drop"  content in there to synchronise with the Dropbox. Usually that is program Photos (~/Pictures), documents (~/Documents), maybe tunes (~/Audio or ~/Music). These locations should be generic since they are not related to any single user application.

"Drag and dropping"  equals "moving" if done on a single partition. When you move a file you also move its context and so we want "~/Dropbox" to have the same type as the content that is expected to be dropped in there.

If a user is stupid enough to drop his " ~/.gnupg" or "~/.ssh" directory into the "~/Dropbox" then the dropbox process type will not be able to access that content. E.g. it wont be able to synchronize your sensitive files.

Unless ofcourse if you really want it to. In that case you would first manually relabel the content to the generic user home content type (user_home_t) with the "chcon" command and them drop it into your Dropbox.

So leaving "~/Dropbox" type "user_home_t"  and allowing Dropbox to manage files and directories with the "user_home_t"  seems to me the sensible thing to do here.

The third rule is a bit more controversial. The  Dropbox archive copies library files into ~/.dropbox-dist. I decided to only give "dropboxd, dropbox and library.zip"  in there a private type for simplicitly but the side effect is the now all dropbox process types need to mmap generic user home content files (the libraries).

The fourth rule is a file type transition rule. The Dropbox installed creates "~/Dropbox" which we want to have type "user_home_t". But this location has a fixed name so we could have used a named file transition rather than a normal file transition. However during installation Dropbox also create a directory in "~"  with a random name andso we could not use a named file type transition for that anyways, and so it was decided to use this rule

Basically it dictates that if any Dropbox process type creates a directory in a directory with type "user_home_dir_t"  (~) that the directory should be created with the "user_home_t" generic user home content type.

The fifth rule allows any dropbox process type to "use"  user terminals. This will allow Dropbox to print any messages to the terminal when you run "./dropboxd"

optional_policy(`
 dbus_session_bus_client(dropbox_domain) # probably not actually optional
 dbus_connect_session_bus(dropbox_domain) # probably not actually optional
')

This will allow anydropbox domain to become a DBUS session bus client and to aquire service on the session bus. Since i was under the assumption that DBUS is not a requirement for Dropbox i wrapped these rules in a "optional_policy"  block that basically tells the policy compiler that these rules are optional. E.g. if they are not available for use then do not sweat it and proceed.


optional_policy(`
 gnome_read_generic_data_home_dirs(dropbox_domain) # searching ~/.local/share
 gnome_read_home_config(dropbox_domain) # ibus, might not be optional

 # hack
 gen_require(`
  type config_home_t;
 ')

 allow dropbox_domain config_home_t:dir setattr_dir_perms;
')

These rules are related to content in X desktop (XDG) standard locations. Basically all Dropbox process types need to be able to read IBUS files in "~/.config" These IBUS files are labeled with the generic "config_home_t"  user home content type.

Dropbox is probably using some Gnome library that does this on behalf of Dropbox. It also wants to set attributes of generic "config_home_t"  directories and search generic "data_home_t"  directories (~/.local/share).

Okay... I think i touched on everything now, except...

How to actually implement this policy

Create a working directory and change directory into it:

mkdir ~/mydropbox; cd ~/mydropbox;
Create three "mydropbox"  source policy files:

touch mydropbox.{te,if,fc}
Copy the policy above in their respective files

Next, create another file:

touch myunconfineduser.te

and paste the following in that file:

policy_module(myunconfineduser, 1.0.0)

gen_require(`
 type unconfined_t;
 role unconfined_r;
')

dropbox_role_template(unconfined, unconfined_r, unconfined_t)

This is what actually calls or if you will activates the "dropbox_role_template()"  that i have created in the "~/mydropbox/mydropbox.if"  interface file.

Now build the two policy packages. You may or may not need to install the "selinux-policy-devel"  package for this:

Make -f /usr/share/selinux/devel/Makefile

Next install the policy packages that were created in "~/mydropbox":

sudo semodule -i mydropbox.pp myunconfineduser.pp

If you have already installed Dropbox then make sure to restore the context of the "~/.dropbox" directories and the "dropboxd, dropbox and library.zip"  files in "~/.dropbox-dist":

restorecon -R -v -F ~
If you have not yet installed Dropbox:

cd ~ && wget -O - "https://www.dropbox.com/download?plat=lnx.x86_64" | tar xzf -
~/.dropbox-dist/dropboxd

Disclaimer:

I do not encourage nor endorse the use of Dropbox. Security means taking responsibility. Do not trust your content to third parties.

maandag 8 oktober 2012

Easy ways to help improve refpolicy contrib

Here is what you can do to help improve refpolicy contrib for your distro and everyone else:

1. See if the file context specification from the various modules in refpolicy contrib match the locations of the corresponding packages that your distribution uses.

Here is how i do that on Fedora:

I basically check each modules file context files.

Find which package installs the executable file, either with the full path or if my package manager cannot find that using a wild card:

yum whatprovides /usr/sbin/someapp
or if it cannot find that:
yum whatprovides *\someapp

Then you should figure out which (applicable) locations that package installs.

Applicable locations are among others:

/etc/someapp /etc/sysconfig/someapp /etc/default/someapp /var/lib/someapp /var/log/someapp /var/spool/someapp /etc/init.d/someapp /etc/rc.d/init.d/someapp /var/run/someapp /var/cache

In Fedora i use the handy repoquery tool for that. This tool allows me to list the files that a specificied package installs without me actually having to install the package

Example:

repoquery -ql someapp | less

 Labeling files properly is very important for SELinux operation and so you can improve your distributions SELinux experience by making use applicable files and locations are labeled correctly by fixing or adding the appropriate file context specification.

2. Somewhat related to (1): label service etc files with a declared private type for etc files and allow the service to read files it owns with the private type, then also allow the "service"_admin() to find and manage content with that type.

In the past we only use to label service etc files with a private"files config file" private type if the file has sensitive information.

Later we came up with an idea to confine root using "service"_admin interfaces that give the caller access to manage a particular service and its files.

However we do not want to give the "service"_admin access to manage generic etc_t files since that will allow the process to manage things it shouldnt, instead of just the configuration files for the service that the caller is allowed to manage.

So we need to find each config file a confined service owns label that with a declared "files_config_file" file type, allow the owning service to read applicable content with that type and allow the service admin to get to and manage content with that type.

You can use the method of (1) to find which etc files a package installs where

Then if needed declare a new files_config_file type

type someapp_conf_t;
files_config_file(someapp_conf_t)

Then make sure the file or directory is labeled correctly in the file context file. Some examples:

Single configuration file:

/etc/someapp\.conf -- gen_context(system_u:object_r:someapp_conf_t,s0)

A configuration directory and all its contents:

/etc/someapp(/.*)? gen_context(system_u:object_r:someapp_conf_t,s0)

Then allow the service domain type to read the content:

in case of a single file:

allow someapp_t someapp_conf_t:file read_file_perms;

in case of a directories and its contents:

read_files_pattern(someapp_t, someapp_conf_t, someapp_conf_t)

If your service domain type is not already allowed to read generic etc_t files (e.g. if it does not have a rule similar to for example: files_read_etc_files(someapp_t) then you will also need to allow the service domain type to traverse etc_t directories so that it can actually get to there:

files_search_etc(someapp_t)

If the module has a service admin interface, for example someapp_admin in the someapp.if interface file, then add the rules that will allow the caller to get to and manage content with the new type:

a. Import the type by adding for example;

type someapp_conf_t; 

to the existing "gen_require(`')" block on top of the interface.

b. Add the rules to allow caller to get to and manage the content with the new type , for example:

files_search_etc($1)
admin_pattern($1, someapp_conf_t)

Note: also consider environment files in /etc/sysconfig. We would want the service administrator to be able to manage those as well.

If you are unsure about some specifics then look into existing source policy modules. Much efford goes into writing these modules as consistently as possible.

This allows you to see patterns easily and will help you read the policy.

zondag 30 september 2012

Determine whether Cron can execute jobs on behalf of the user with login user SELinux permissions

Cron would run jobs on behalf of users in a "cronjob" domain. This domain is reasonable restricted compared to the domain in which most users operate.

Fedora changed this behaviour to allow Cron to execute these jobs in the default login user domain of the user that owns the job.

Recently i added a boolean to Reference policy that enables one to tune the policy to allow Cron to run user cronjobs in the default login user domain conditionaly.

By default Cron will still execute the jobs in the Cron job domain but if you toggle the cron_userdomain_transition boolean to true then Cron will run the jobs in the default login user domain of the user owning the job.

This allows Cron to run jobs with the SE-Linux permissions that the owner of the job has.

Cron is SELinux aware. It queries the policy to see to which domain it should transition when running a job.

It does not do a automatic domain transition because it does not actually execute the Cron job. Instead if uses "setexec" to domain transition.

It looks to see to which domains the Cron job file is a entrypoint. Then it needs to be able to process transition to that domain and then it checks the default login user domain of a user and with this information it determines in which domain to execute the job

By default:

allow cronjob_t user_cron_spool_t:file entrypoint;
allow crond_t cronjob_t:process transition;

Then there is a default context of for staff this is for example:

system_r:crond_t:s0        staff_r:cronjob_t:s0

But when you tune the policy with cron_userdomain_transition then these rules will be replaced by:

allow $1 user_cron_spool_t:file entrypoint;
allow crond_t $1:process transition;

Where $1 is the calling login user domain, and then it will look up the default login user domain and role, for staff this is for example:

# semanage user -l | grep staff_u
staff_u         user       SystemLow  SystemLow-SystemHigh           staff_r sysadm_r system_r unconfined_r
That means:

"staff_r:staff_t"

Then Cron daemon has permission to run the job in the staff domain with the staff role.

The benefits of running jobs in the login user domain are that now your Cron job can interact with your processes and operate on your files as if it were you interacting with your processes and operating on your files.

Needless to say that other SE-Linux rules that apply to you now also apply to your Cron jobs.

The policy may still have some rough edges but it was tested on Gentoo and is said to work. I did make some changes after that but hopefully i have not introduced regression.

Currently Redhat distributions do not have this cron_userdomain_transition boolean and i am not sure if she will adopt it.

woensdag 27 juni 2012

Hard coded types create hard dependencies on selinux policy

Long time no see. I believe that applications like sandbox (sandbox_web_t) and sshd (chroot_user_t) hard code types. I do not like that but my personal opinion aside; This creates hard dependencies on SELinux policy. If you do not have a policy that provides these types then you will end up with broken functionality. I believe one example is that sandbox does not work in Debian because Debian selinux-policy does not provide the types that sandbox requires. That is all folks.