Macros in SELinux are discussed in Section 2.9 Policy Macros. This section covers macros that are used extensively throughout the targeted policy. These were chosen by frequency, the list comprising mainly macros used nine or more times in the various policy files. A large number of macro files are present in the policy, but are not necessarily called by any of the TE files. There are macro files for many, but not all, of the targeted daemons.
The macro daemon_domain is in $SELINUX_SRC/macros/global_macros.te, and is common to all of the targeted daemons. The purpose of daemon_domain is to group together permission needs common to all daemons. These needs include creating a process ID (PID) file and running df to check disk usage. In addition, two macros are called, daemon_base_domain and read_locale.
The base common set of type declarations and permissions is defined in daemon_base_domain, and include allowing you to define a tunable that can disable the domain transition. You evoke one of these tunables when you set the Boolean value to disable the transition to one of the targeted domains, removing SELinux protection from that single daemon. Finally, daemon_core_rules is called.
This central macro is where the daemon's top-level domains and roles are declared:
define(`daemon_core_rules', ` type $1_t, domain, privlog $2; type $1_exec_t, file_type, sysadmfile, exec_type; |
daemon_core_rules gives a daemon the right to inherit and use descriptors from init, calls the uses_shlib() macro for the domain to use shared libraries, allows for common self signaling, and so forth.
Providing a top-level entry point for common networking policy, this macro appears in $SELINUX_SRC/macros/global_macros.te. One primary allow rule gives the domain access to TCP and UDP sockets to create, send and receive on a network interface from any node on any port. Read permission is granted for network files, which are configuration files in /etc/ that network daemons need, mainly /etc/resolv.conf:
# can_network(domain) define(`can_network',` allow $1 self:udp_socket create_socket_perms; allow $1 self:tcp_socket create_stream_socket_perms; allow $1 netif_type:netif { tcp_send udp_send rawip_send }; allow $1 netif_type:netif { tcp_recv udp_recv rawip_recv }; allow $1 node_type:node { tcp_send udp_send rawip_send }; allow $1 node_type:node { tcp_recv udp_recv rawip_recv }; allow $1 port_type:{ tcp_socket udp_socket } { send_msg \ recv_msg }; ... allow $1 net_conf_t:file r_file_perms; ')dnl end can_network definition |
The limitations on which nodes and ports are allowed by domain are defined in separate rules. Recall that by default, everything is denied in SELinux. The allow rules here grant only the permission to, for example, make a bind(2) call to the socket, but the specific port binding requires another authorization. The permission name_bind to bind to the port is still limited by the domain, so that, for example, named may be allowed by standard Linux permissions to bind to port 22, but SELinux blocks access and generates an avc: denied message in $AUDIT_LOG. This is because named_t is not allowed to bind to a port of type ssh_port_t, which is the type for the SSH port.
This popular macro from core_macros.te provides permissions for establishing a UNIX stream connection:
# can_unix_connect(client, server) define(`can_unix_send',` allow $1 $2:unix_dgram_socket sendto; ') |
The can_unix_connect macro handles the control between the two domains. In addition, the socket needs write permission to the file associated with the socket. For this reason the can_unix_connect macro is paired with other allow rules in the policy:
# From $SELINUX_SRC/domains/program/syslogd.te allow privlog devlog_t:sock_file rw_file_perms; ... can_unix_connect(privlog, syslogd_t) |
Common permissions for creating directories, regular files, and symlinks are grouped into this macro from core_macros.te:
define(`create_dir_file', ` allow $1 $2:dir create_dir_perms; allow $1 $2:file create_file_perms; allow $1 $2:lnk_file create_lnk_perms; ') |
This is a single line macro from core_macros.te that expands into a list of permissions needed to create and manage sockets. This macro is inserted directly into a rule, and gives a single location to define this common permissions set. This is the macro followed by an example rule evoking the macro:
define(`create_socket_perms', `{ create ioctl read getattr \ write setattr append bind connect getopt setopt shutdown }') allow winbind_t self:unix_dgram_socket create_socket_perms; |
When you want a particular transition to be the default transition behavior for a domain, use domain_auto_trans, from core_macros.te. It calls domain_trans() to do the actual work. If you just want to allow a transition to take place and handle the context with setexeccon(), you can use just a domain_trans():
# domain_auto_trans(parent_domain, program_type, child_domain) define(`domain_auto_trans',` domain_trans($1,$2,$3) type_transition $1 $2:process $3; ') |
The domain_trans macro allows the parent process a typical set of permissions to transition to the new domain. It sets a few dontaudit rules, allows the parent process to execute the program, allows the child to reap the new domain and exchange and use file descriptions with the parent process, as well as write back to the old domain via a named pipe (FIFO). Finally, the new domain is given permission to read and execute the program, making the program the entry point for the domain.
Because only one transition can be the default for a given pair of types, you can have one domain_auto_trans rule followed by multiple domain_trans rules that allow other options. These can be used by a security-aware application.
From core_macros.te, file_type_trans allows the permissions for the given domain to create files in the given parent directory that have the given file type. These givens are inserted as the variables $1 and so forth. To make a particular transition the default behavior, use file_type_auto_trans().
This macro has a built-in conditional that it can take three or four parameters in defining the output type_transition rule:
# file_type_auto_trans(creator_domain, parent_directory_type, \ file_type, object_class) # # the object class will default to notdevfile_class_set if not # specified as the fourth parameter define(`file_type_auto_trans',` ifelse(`$4', `', ` file_type_trans($1,$2,$3) type_transition $1 $2:dir $3; type_transition $1 $2:notdevfile_class_set $3; ', ` file_type_trans($1,$2,$3,$4) type_transition $1 $2:$4 $3; ')dnl end ifelse ') |
The file_type_trans allows the process to modify the directory and create the file, all with the proper labeling. As with domain_auto_trans, you can specify additional allowed options for use by security-aware applications that can call setexeccon().
The optional fourth parameter, $4, lets you specify a particular file object class. The default is non-device files, notdevfile_class_set().
These single line macros from core_macros.te are used directly in TE rules to group common permission sets depending on the need to read, write, append, and execute files and directories:
# Permissions for reading directories and their attributes. # define(`r_dir_perms', `{ read getattr lock search ioctl }') # # Permissions for reading files and their attributes. # define(`r_file_perms', `{ read getattr lock ioctl }') # # Permissions for reading and writing files and their # attributes. # define(`rw_file_perms', `{ ioctl read getattr lock write \ append }') # # Permissions for reading and appending to files. # define(`ra_file_perms', `{ ioctl read getattr lock append }') |
This macro identifies the domain as needing to be able to create temporary files in /tmp/. A file transition is setup for temporary files created by the domain. A separate temporary type, $1_tmp_t, is declared. This type is used to label temporary files created by the domain so that each domain may only read and write its own temporary files. Additional rules may be written that allow permissions for domains to control temporary files of other domains, such as allowing tmpwatch to clean up /tmp/. Most of the targeted daemons use this macro:
define(`tmp_domain', ` type $1_tmp_t, file_type, sysadmfile, tmpfile $2; file_type_auto_trans($1_t, tmp_t, $1_tmp_t) ') |