7.3 Type-Enforcement Declarations
Type-enforcement (TE) declarations are of seven types:
- attribute_def
Attribute declarations
- type_def
Type declarations
- typealias_def
Type alias declarations
- bool_def
Boolean declarations
- transition_def
Transition declarations
- te_avtab_def
TE access vector table declarations
- cond_stmt_def
Conditional statement declarations
7.3.1 Type Declarations
The SELinux policy language requires that
all type names be explicitly defined. In the simplest possible form,
a type declaration merely defines a name as a type. For instance, the
type declaration:
type ping_t;
would mark ping_t as the name of a type. Type
declarations need not precede all statements that refer to the types
they define; you can place type declarations any place within a TE
file.
Optionally, a type declaration may define one or more
aliases for the type name. Any
alias associated with a type can be freely used in place of the
primary name of the type. A type declaration can also optionally
associate one or more attributes with the type name.
Figure 7-1 shows the syntax of a type declaration.
As an example, the ping.te file contains two
type declarations:
type ping_t, domain, privlog;
type ping_exec_t, file_type, sysadmfile, exec_type;
The first declaration identifies ping_t as a type
name, and associates the attributes domain and
privlog with the type name, marking the type as a
domain that communicates with the system log process. The second
declaration identifies ping_exec_t as a type name,
and associates the attributes file_type,
sysadmfile, and exec_type with
the type name, marking the type as one used to identify executable
files accessible by system administrators.
To better understand how type attributes work with types, consider
the definition of the
syslogd domain, which contains the following
declarations:
allow privlog devlog_t:sock_file { ioctl read getattr lock write append };
allow privlog syslogd_t:unix_dgram_socket sendto;
allow privlog syslogd_t:unix_stream_socket connectto;
allow privlog devlog_t:lnk_file read;
Notice how the type attribute privlog
is used in these declarations in the same way that an actual type
name might be used. Type attributes differ from types in that type
attributes generally appear in multiple domains, whereas each type
generally appears only in a single domain. You can think of a type
attribute as simply an abbreviation standing for a set of access
vector rules. You can associate these access vector rules with a type
simply by binding the type attribute with the type, just as the
domain and privlog type
attributes are bound to the ping_t type.
The four allow declarations given earlier specify
the range of permissible operations associated with the
privlog type attribute, specifically:
Perform various read and write operations on socket files having type
devlog_t. Send data to datagram and stream sockets having type
syslogd_t. Read files and symbolic links having type devlog_t.
As you'd likely guess, the types
devlog_t and syslogd_t are used
to label system log files, system log FIFOs, and the sockets used to
communicate with the syslog process.
|
The meanings of type attributes such as domain and
privlog are not hardcoded in SELinux. Instead, the
meaning of a type attribute is determined by policy statements.
Consequently, the administrator of an SELinux system can create new
type attributes or modify the meaning of type attributes that appear
in sample policies. If the administrator of an SELinux system has
added or modified many type attributes, it may be difficult to
determine their meanings, as doing so would involve reading all the
policy declarations related to each customized type attribute.
Fortunately, the set of type attributes defined in sample policies is
rich, and system administrators generally do not need, or choose, to
extend or modify it substantially. And most domain attributes have
names that suggest their meaning. Appendix E, explained in the
upcoming section titled "Attribute
Declarations," summarizes the meanings of principal
SELinux type attributes.
|
|
7.3.2 Type-Alias Declarations
As explained
in the preceding section, a type
declaration can optionally bind one or more aliases to a type name.
However, it's more common to use a special
type-alias declaration to establish such a binding. Figure 7-2 shows the syntax of type-alias declarations.
Many M4 macros generate
type alias declarations. However, a few TE files contain explicit
type-alias declarations. For instance, the
cups.te file contains the follow type-alias
declaration:
typealias cupsd_etc_t alias etc_cupsd_t;
This declaration defines etc_cupsd_t as an alias
for the type name cupsd_etc_t, allowing the two
names to be used interchangeably.
7.3.3 Attribute Declarations
A type attribute is a name that is
bound to one or more types and used to define a set of types sharing
some property. For instance, a type attribute can be used to
designate the types (domains) that are allowed to read the system log
file.
Because type attributes can appear in allow
declarations just as though they were types, permissions can be
granted by referring either to types or type attributes. An
allow declaration containing a type attribute
refers to all types associated with the type attribute. Thus, type
attributes make it convenient to specify policies that apply to
multiple types. The relationship between types and attributes is a
many-to-many relationship; an indefinite number of types can be
associated with an attribute, and an indefinite number of attributes
can be associated with a type.
Type attributes are defined in the file attrib.te.
Appendix E
summarizes the type attributes defined in the Fedora
Core 2 implementation of SELinux.
Figure 7-3 shows the syntax of an attribute
declaration, the SELinux policy statement that defines a type
attribute. As you can see, the syntax is quite simple. A typical
declaration is:
attrib admin;
This declaration identifies admin as an attribute.
Appendix E explains that this attribute is used to identify
administrator domain�domains that should be available only to
system administrators.
|
Recall that the term domain refers to a type
associated with a process.
|
|
7.3.4 TE Access-Vector Declarations
The permissions enforced by the SELinux security
engine are held in kernel data space in an object known as the TE
access matrix. As explained in Chapter 2, the
TE access matrix includes four distinct access components, called
vectors:
- allow
Operations that are allowed but are not
logged
- auditallow
Operations that are allowed and are
logged when they occur
- auditdeny
Operations that are denied and are logged
when they are attempted
- dontaudit
Operations that are denied, but are not
logged when they are attempted
Each TE access vector contains TE access-vector rules. A TE
access-vector rule specifies permissible operations�based on a
source type, a target type, and a security object class. Whenever an
operation is attempted, the SELinux security engine searches the
access vectors for a rule matching the source type, target type, and
object class of the operation. If a matching rule is found, the
access vector containing the rule determines the action taken by
SELinux. For instance, if the matching rule resides in the
allow access vector, the operation is allowed.
However, if the matching rule resides in another access vector, or no
matching rule exists, the operation is denied.
Figure 7-4 shows the syntax of
access-vector rules. Notice that the
diagram shows what seems to be a fifth type of access-vector rule,
represented by the
neverallow rule type. The
neverallow rule defines constraints that the
SELinux policy must observe. The policy compiler checks for
violations of these constraints, and if it finds any violations, it
terminates without producing a binary policy file. You can add or
subtract neverallow rules from the SELinux policy.
However, they're intended as a safety feature that
prevents you from generating a grossly insecure policy, so
it's generally best not to disturb them.
Each of the five forms of access vector rules contains four terms:
The source type or types; this is generally the type associated with
the process attempting to perform an operation. The target type or types; this is generally the type associated with
the object that the process is attempting to manipulate. The object class or classes to which the rule applies. The permissions that the rule establishes.
The syntax of each of these terms is represented by the replaceable
text names, which was explained in Chapter 6 and represented in Figure 6-13.
Here's a sample allow
declaration associated with the
ping_t domain:
allow ping_t ping_exec_t:file { read getattr lock execute ioctl };
The rule created by this declaration allows processes running in the
ping_t domain to perform any of five operations
(read, getattr,
lock, execute, and
ioctl) on files labeled as belonging to the
ping_exec_t domain.
|
If you check the ping.te file, you
won't find this declaration there. Many
access-vector declarations, including this one, are created by
expansion of M4 macros, such as the
domain_auto_trans macro explained later in this
section.
|
|
The standard SELinux security policy includes fewer than six
auditallow
declarations. Here's a sample declaration:
auditallow kernel_t security_t:security load_policy;
Recall that auditallow rules
don't actually enable any operations. Therefore,
this rule is supplemented by an allow rule such
as:
allow kernel_t security_t:security load_policy;
The allow rule authorizes processes running in the
kernel_t domain (that is, kernel processes) to
perform the load_policy operation on
security objects labeled with the
security_t domain. More plainly, the
allow rule allows the kernel to load an SELinux
policy. The auditallow rule causes every such
operation to be logged when it is performed. Thus, the system log
contains a record of these important events.
The standard SELinux security policy does not include even one
instance of an auditdeny rule. Since the default
action of SELinux is to forbid unauthorized operations and make a log
entry documenting the action,
auditdeny
rules are not generally needed. However, so that you can see how
auditdeny rules work, here's a
hypothetical auditdeny rule declaration:
auditdeny user_t security_t:security load_policy;
The rule created by this declaration forbids processes running in the
user_t domain from performing the
load_policy operation on
security objects labeled with the
security_t domain.
Here's a sample declaration of a
dontaudit rule:
dontaudit ping_t var_t:dir search;
The rule created by this declaration suppresses log entries when
processes in the ping_t domain attempt to search a
directory labeled with the var_t domain, and are
prohibited by the SELinux security engine from doing so.
|
It's somewhat common for programs to attempt
operations on security-sensitive objects, even though they
don't need to do so. The
dontaudit rule enables you to suppress such
operations and avoid cluttering the log with a flood of routine
entries associated with them.
|
|
As explained, the
neverallow
rule type enforces constraints on the SELinux security policy itself.
It helps ensure the integrity of the policy, which might be
inadvertently weakened by well-intentioned but erroneous changes.
Operations forbidden by a neverallow constraint
are prohibited even if a conflicting
allow rule exists within the
SELinux security policy.
Here's a hypothetical neverallow
constraint declaration:
neverallow domain file_type:process transition;
The constraint created by this declaration would prevent a process
having the domain attribute from transitioning to
a type having the file_type attribute. The
constraint would prevent an errant policy modification that allowed a
process to be treated as a file, which might compromise system
security.
7.3.4.1 Special notations for types, classes, and permissions
The hypothetical
neverallow constraint just given is effective but
incomplete. Ideally, we'd prohibit transitions from
a domain type to any non-domain
type, not just every type marked as a file_type. A
special notation associated with the replaceable text
names enables us to do so:
neverallow domain ~domain:process transition;
The constraint associated with this declaration forbids a process
having the domain type attribute from
transitioning to a type not having that attribute. For instance, the
constraint prevents a malicious user from causing a process to
transition to a file or another nondomain object.
Notice that the target type is specified as
~domain. This notation, known as
complementation,
provides a convenient means of referring to types that do
not possess a specified attribute. In this case, the
declaration refers to types that do not have the
domain attribute. If complementation were not
available, we'd find it cumbersome to write a
constraint such as this, since the constraint would have to refer
explicitly to every type that is not a domain. Many such types might
exist. Moreover, having added a new nondomain type to the policy, we
might neglect to modify the constraint appropriately. So
complementation provides an important convenience.
Another convenient notation is known as
subtraction.
Here's an example of a constraint declaration that
employs subtraction:
neverallow { domain -admin -anaconda_t -firstboot_t -unconfined_t -kernel_t
-load_policy_t } security_t:security load_policy;
The constraint created by this declaration prevents any domain other
than those listed with minus signs (admin,
anaconda, firstboot_t,
unconfined_t, kernel_t, and
load_policy_t) from performing the
load_policy operation on a
security object having type
security_t.
Occasionally, it's necessary to refer to
all types, classes, or permissions. The asterisk
(*) can be used to do so, as in the following constraint declaration:
neverallow domain file_type:process *;
The constraint created by this declaration prohibits any type having
the domain attribute from performing
any operation on processes having the
file_type attribute.
Here's a somewhat more interesting example that
includes two instances of the asterisk operator:
neverallow ~{ domain unlabeled_t } *:process *;
The constraint created by this declaration prevents types other than
those having attributes domain or
unlabeled_t from performing
any operation on processes having
any type.
Another special notation enables us to create rules where the target
type is the same as the source type. Here's an
example:
neverallow {domain -admin -insmod_t -kernel_t -anaconda_t -firstboot_t -unconfined_t }
self:capability sys_module;
The rule created by this declaration applies to domains other than
six specific domains (admin,
anaconda, firstboot_t,
unconfined_t, kernel_t, and
load_policy_t). It prohibits these domains from
performing the sys_module operation on
capability objects labeled with domains other than
the source domain.
These special notations can be used with allow
rules as well as neverallow rules. For instance,
consider the following rule:
allow sysadm_t self:process ~{ ptrace setexec setfscreate setrlimit };
This rule lets processes within the sysadm_t
domain perform any operation on processes running within that domain,
with the exception of the operations listed:
ptrace, setexec,
setfscreate, and setrlimit.
Table 7-1 summarizes the special notations used to
specify types, classes, and permissions.
Table 7-1. Special notations for specification of types, classes, and permissions
Notation
|
Description
|
---|
*
|
All members
|
~
|
Complementation
|
-
|
Subtraction
|
self
|
Target type same as source type
|
|
In addition to special notations, macros can be used to specify
types, classes, or permissions.
Appendix C summarizes a set of such
macros, defined in the file
macros/core_macros.te.
It's not necessary to specify all authorized
permissions in a single access-vector rule. SELinux combines
permissions pertaining to the same source type, target type, and
object class into a single access-vector rule that authorizes all
specified operations.
|
|
7.3.4.2 Macros that specify and authorize transitions
Two main types of rules govern transitions:
- Type-transition rules
Specify
transitions that occur
when a new object, such as a process or file, is created.
- Access-vector rules
Authorize
transitions.
Type-transition rules do not authorize the transitions they specify.
That is, they specify the transition that would
occur, but don't actually give permission for the
transition to occur. An access-vector rule must authorize the
transition, or it will not be allowed to occur. Therefore, type
transition and access vector rules both must be specified for most
transitions. To avoid the associated tedium, several M4 macros
conveniently generate type transition and access vector rule
declarations from a single line of policy source code. Generally, the
most useful of these macros are:
- domain_auto_trans
Specifies and authorizes a transition
related to the execution of a program defined as a domain entry
point.
- file_type_auto_trans
Specifies and authorizes a transition
related to file creation.
For instance, the ping.te file of the
Fedora Core 2 SELinux implementation invokes the
domain_auto_trans macro three times:
domain_auto_trans(unpriv_userdomain, ping_exec_t, ping_t)
domain_auto_trans(sysadm_t, ping_exec_t, ping_t)
domain_auto_trans(initrc_t, ping_exec_t, ping_t)
The first invocation is executed conditionally, as explained in the
next section. The second and third invocations are executed
unconditionally. Each invocation defines a transition from a domain
(unpriv_userdomain, sysadm_t,
or initrc_t) to the ping_t
domain when a ping_exec_t executable is loaded.
The transition is also authorized by an access vector rule. For
instance, the third invocation expands to the following policy
declarations:
type_transition initrc_t ping_exec_t:process ping_t;
allow initrc_t ping_t:process transition;
Here's an example of a typical use of the
file_type_auto_trans macro, occurring in the
ftpd.te file of the Fedora Core 2 SELinux
implementation:
file_type_auto_trans(ftpd_t, var_log_t, xferlog_t, file)
This macro invocation expands to the following policy declarations:
type_transition ftpd_t var_log_t:file xferlog_t;
allow ftpd_t var_log_t:dir rw_dir_perms;
allow ftpd_t var_log_t:file create_file_perms;
The file_type_auto_trans macro simplifies
definition of the ftpd_t domain, by using a
one-line macro invocation to specify that:
When an ftpd_t process creates a file in a
directory having type var_log_t (such as
/var/log), the file should be given the type
xferlog_t. Thus, the FTP logs
/var/log/xferlog and
/var/log/xferreport will be properly labeled
when created. Any ftpd_t process can read and write
var_log_t directories (that is, perform any of the
following operations associated with rw_dir_perms:
read, getattr,
lock, search,
ioctl, add_name,
remove_name, and write). Any ftpd_t process can create files within
var_log_t directories (that is, perform any of the
following operations associated with
create_file_perms: create,
ioctl, read,
getattr, lock,
write, setattr,
append, link,
unlink, and rename).
Occasionally, it's convenient to specify, but not
authorize, a transition because the transition is already authorized
elsewhere in the policy file or in another policy file. The following
macros do so:
- domain_trans
Specifies, but does not authorize, a
transition related to execution of a program defined as a domain
entry point.
- file_type_trans
Specifies, but does not authorize, a
transition related to file creation.
7.3.5 Transition Declarations
A transition rule specifies the
new domains for a process or the security contexts for a newly
created object, such as a file. Each transition rule has two types: a
source type and a target type. For a process, the source type is the
current domain of the process, and the target type is the type of the
executable file. For an object, the source type is the domain of the
process creating the object, and the target type is the type of a
related object. For instance, if the object is a file, the target
type is the type of the file's parent directory.
Figure 7-5 shows the syntax of
type transitions. The railroad diagram
contains three instances of replaceable text, each having the
syntax of the now-familiar replaceable text names,
originally described in Chapter 6 and
represented in Figure 6-13:
The diagram also includes the replaceable text
new_type, which has the syntax of the familiar
replaceable text identifier. The replaceable text
new_type specifies the new type of the process or
object.
Here's a typical type transition rule pertaining to
a process:
type_transition sysadm_t ping_exec_t:process ping_t;
This rule affects the behavior of processes in the
sysadm_t domain that execute a program having type
ping_exec_t. Executing such a program causes the
process to attempt to transition to the
ping_t domain. I write attempt
to because SELinux does not authorize operations,
including transitions, by default. So if the transition is to
succeed, an access-vector rule must authorize it; otherwise, unless
SELinux is operating in permissive mode, the SELinux security engine
will prohibit the transition.
Here's a typical type-transition rule pertaining to
a file:
type_transition httpd_t var_log_t:file httpd_log_t;
This rule affects the behavior of processes in the
httpd_t domain that create a file having a parent
directory of the var_log_t type. Such files are
created with the httpd_log_t type, unless the
appropriate access vector rule does not exist.
The replaceable text identifier is always
associated with a single identifier, whereas the replaceable text
names can be associated with multiple identifiers,
as shown in Figure 6-13. Because the railroad
diagram refers to three instances of names and one
instance of identifier�rather than four
instances of identifier�a transition
declaration can refer to multiple source types, target types, or
classes. Here's a typical rule that does so:
type_transition httpd_t tmp_t:{ file lnk_file sock_file fifo_file } httpd_tmp_t;
Like the preceding rule, this rule also affects the behavior or
processes in the httpd_t domain. When such a
process creates a file,
lnk_file, sock_file, or
fifo_file object having a parent object of type
tmp_t, the new object receives the type
http_tmp_t, unless the appropriate access vector
rule does not exist.
Because several sets of class names are commonly used, the file
macros/
core_macros.te
defines eight convenient M4 macros, described in Table 7-2. Using the appropriate macro, the preceding
type transition rule could be written more compactly as:
type_transition httpd_t tmp_t:notdevfile_class_set httpd_tmp_t;
Table 7-2. Class name M4 macros
Macro
|
Definition
|
Description
|
---|
devfile_class_set
|
{ chr_file blk_file }
|
Device file classes
|
dgram_socket_class_set
|
{ udp_socket unix_dgram_socket }
|
Datagram socket classes
|
dir_file_class_set
|
{ dir file lnk_file sock_file fifo_file chr_file blk_file }
|
Directory and file classes
|
file_class_set
|
{ file lnk_file sock_file fifo_file chr_file blk_file }
|
File classes except dir
|
notdevfile_class_set
|
{ file lnk_file sock_file fifo_file }
|
File classes except dir and device files
(chr_file, blk_file)
|
socket_class_set
|
{ tcp_socket udp_socket rawip_socket netlink_socket packet_socket unix_stream_socket unix_dgram_socket }
|
Socket classes
|
stream_socket_class_set
|
{ tcp_socket unix_stream_socket }
|
Stream socket classes
|
unpriv_socket_class_set
|
{ tcp_socket udp_socket unix_stream_socket unix_dgram_socket }
|
Unprivileged socket classes except raw IP socket class
|
7.3.6 Boolean Declarations
The Fedora Core 2 implementation of
SELinux introduced a new feature: Boolean declarations. A Boolean is
a true-false value that can be tested by policy statements. As
explained in Chapter 4, the
setbool command can
set the value of a Boolean. Booleans make it possible to tailor
dynamically the behavior of an SELinux policy.
Figure 7-6 shows the syntax of a Boolean
declaration. The Fedora Core 2 SELinux policy defines one Boolean,
user_ping:
bool user_ping false;
This Boolean controls nonprivileged user access to the
ping
and traceroute commands. This control is
implemented by conditional declarations included in the
ping.te and traceroute.te
files, as explained in the next section.
7.3.7 Conditional Declarations
The declarations explained so far in
this chapter have been unconditional declarations. Recent
implementations of SELinux, such as that included in Fedora Core 2,
also support conditional declarations. A simple conditional
declaration has two parts:
A Boolean expression that is evaluated when the security engine makes
policy decisions. A declaration that takes effect only if the Boolean expression
evaluates true. The declaration is referred to as a subdeclaration,
because it occurs inside the conditional declaration.
A more sophisticated conditional declaration includes a Boolean
expression and two alternative subdeclarations. Depending on the
result of dynamically evaluating the Boolean expression, the
declaration has the force of either of the two subdeclarations.
Figure 7-7 shows the syntax of a conditional
declaration. As the figure shows, the syntax of the associated
conditional expression (cond_expr) is rich. The
subdeclaration or subdeclarations have a familiar form, that of
either a type transition or access-vector declaration of the
following kinds:
allow auditallow auditdeny dontaudit
|
Although the railroad diagram in Figure 7-7
indicates that a subdeclaration can be an
auditdeny declaration, SELinux does not support
such subdeclarations at the time of writing. However, you can express
equivalent policies by using one or more other declaration types
rather than an auditdeny.
|
|
The conditional expression within a conditional statement declaration
can use any of six relational operators, summarized in Table 7-3.
Table 7-3. Relational operators
Symbol
|
Description
|
---|
&&
|
Logical AND
|
==
|
Logical equality
|
!
|
Logical negation
|
!=
|
Logical inequality
|
||
|
Logical OR
|
^
|
Logical exclusive OR
|
Here's a sample conditional statement declaration,
taken from the ping.te file
associated with the Fedora Core 2 implementation of SELinux:
if (user_ping) {
domain_auto_trans(unpriv_userdomain, ping_exec_t, ping_t)
# allow access to the terminal
allow ping_t { ttyfile ptyfile }:chr_file rw_file_perms;
ifdef(`gnome-pty-helper.te', `allow ping_t gphdomain:fd use;')
}
The user_ping conditional expression
refers to a policy Boolean that indicates whether nonprivileged users
are authorized to use the ping and
traceroute commands, as explained earlier in
this chapter. Only if the Boolean has the value
true do the subdeclarations have effect. The
subdeclarations:
Authorize an automatic transition from a domain marked as an
unpriv_userdomain to the ping_t
domain upon execution of a ping_exec_t program. Authorize the ping_t domain to access the
user's TTY or PTY. Invoke an M4 macro, ifdef, that conditionally
allows the ping_t domain to use file descriptions
passed by the Gnome PTY helper (gphdomain)
domain.
|