Section 5.3. Access Vector Rules


5.3. Access Vector Rules

AV rules are those rules that specify their meaning in terms of access permissions[1] for object classes. The SELinux policy language currently supports four types of AV rules:

[1] In the code, the set of permissions for an object class are represented by a bit mask called an access vector, hence the term access vector rule.

allow

Specifies access allowed between two types

dontaudit

Specifies access denial messages to not record

auditallow

Specified access allowed events to record

neverallow

Specifies access permissions that may never be granted by any allow rule


We examine each of these rules, their common and unique syntax and semantics, and examples of their usage in the remainder of this section. The common syntax for all AV rules is shown in the sidebar on page 107.

5.3.1. Common AV Rule Syntax

Although each of these AV rules has a different purpose, they all have the same basic syntax. Each rule contains five elements:

Rule name

allow, dontaudit, auditallow, or neverallow

Source type(s)

The type(s) being granted access, usually the domain type of a process attempting access

Target type(s)

The type(s) of an object to which the source is being granted access

Object class(es)

The class(es) of object(s) that the specified access is permitted

Permission(s)

The specific access permissions that the source is allowed to the target type for the indicated object classes (also called the access vector)


A simple AV rule has one source type, target type, object class, and permission. We have seen many examples of such AV rules in our earlier allow rule examples, such as the following:

allow user_t bin_t : file execute;


This allow rule has the source type user_t, target type bin_t, object class file, and permission execute. This rule is commonly read as "allow user_t to execute files of type bin_t."

All four AV rules have exactly the same syntax with a different rule name keyword. For example, we could convert the previous example into an auditallow rule by simply replacing the rule name:

auditallow user_t bin_t : file execute;


We will discuss the meaning of this rule later; what is important at the moment is to understand that the syntax is exactly the same.

5.3.1.1. AV Rule Keys

Within the kernel, all the AV rules are uniquely identified by a triplet of source type, target type, and object class. This triplet is called a key for its use as a hash table and cache key within the policy data structures. Recall from Chapter 3, "Architecture," that rules are stored and looked up by this key. When a process makes an access attempt, the SELinux LSM module is queried for the allowed access based on this key.

So, what happens when there is more than one AV rule with the same key (that is, same source type, target type, and object class)? For example, consider a policy with the following rules:

allow user_t bin_t : file execute; allow user_t bin_t : file read;


Are processes of type user_t allowed read or execute access to files of type bin_t? The answer is both; all rules with the same key are combined by checkpolicy. The compiled policy will contain a single rule with both the execute and read permissions, and both will be allowed by the security server. All the AV rules are additive in this way.

Warning

Each subsequent AV rule in a policy that has the same keys as a previous AV rule adds permissions to the ultimate rule compiled into the policy. There is no concept of removing permissions granted by another rule. So be careful; although you might have written a nice tight rule in one part of the policy, another rule elsewhere in a policy (possibly for an attribute that is associated with your type) might grant additional permissions.


5.3.1.2. Using Attributes in AV Rules

Although the AV rules that we have seen so far have been simple, the syntax supports many ways to list types, object classes, and permissions, giving us flexibility and often making the rule statements more concise.

In the simple form of the rules in the previous examples, the rules have referred directly to the source type (user_t) and the target type (bin_t). It is often convenient, however, to refer to multiple types in the source or target of the rules. One way to refer to multiple types is to use attributes. We can use an attribute anywhere we can use a type in AV rules.

For example, suppose we defined an attribute (exec_type) that we plan to associate with all file types that an ordinary user program (indicated by the domain type user_t) may execute. Now we can change our above example to refer to the attribute exec_type rather than an explicit type such as bin_t, as shown here:

allow user_t exec_type : file execute;


Unlike the previous example, this rule does not directly reflect what will be enforced by the kernel. Rules that include attributes will be expanded within the kernel into a separate key for each type associated with the attribute. If there were 20 file types associated with the attribute exec_type, for example, the kernel AVC may end up with 20 keys and associated rules, each one granting execute access for file object class to the type user_t for each of the 20 file types associated with the attribute exec_type.

We can also use attributes as the source of an AV rule, or for both the source and target of the rule. For example, suppose we also created an attribute (domain) that we associated with all domain types (including user_t), and that we want to allow all domain types the ability to execute file types that have the attribute file_type. We can achieve this goal with a single rule:

allow domain exec_type : file execute;


To better illustrate the rule expansion concept, suppose that our policy associated the domain attribute with the types user_t and staff_t, and the exec_type attribute with the file types bin_t, local_bin_t, and sbin_t. Thus, the single rule above would be the equivalent to the following explicit rules:

allow user_t bin_t : file execute; allow user_t local_bin_t : file execute; allow user_t sbin_t : file execute; allow staff_t bin_t : file execute; allow staff_t local_bin_t : file execute; allow staff_t sbin_t : file execute;


5.3.1.3. Multiple Types and Attributes in AV Rules

We are not limited to a single type or attribute for the source and target fields. Rather, we can also list multiple types or attributes as source and target. When there is more than one type or attribute, a space-separated list enclosed in braces is used, as follows:

allow user_t { bin_t sbin_t } : file execute;


In this rule, the target is both bin_t and sbin_t. Rules with multiple types or attributes in the source or target are expanded in the same ways as single attributes. In the previous example, the kernel policy would contain two keys, one each for the type target types.

We can mix types and attributes for either source or target fields, or both. For example, the following rule is perfectly legal:

allow {user_t domain} {bin_t file_type sbin_t} : file execute ;


It is fine if we explicitly list a type and an attribute that the type has. In this case, we have essentially listed the type twice. The kernel will resolve the redundancy and include only one instance of the rule for each combination of source and target types.

5.3.1.4. The Special Type self

The policy language has a reserved word self that acts like a type when used in the target field of an AV rule. For example, the following two rules are equivalent:

# These two rules are equivalent to each other allow user_t user_t : process signal; allow user_t self : process signal;


The keyword self simply means to instantiate a rule for each source type, so that the source and target are the same. In the preceding example, the second rule just creates a key with the source and target both user_t.

Let's look at a slightly more complicated example:

allow {user_t staff_t} self : process signal;


In this example, the rule creates two rules, one each for each source type. This rule is exactly equivalent to the following two rules:

# These two rules... allow user_t user_t : process signal; allow staff_t staff_t : process signal; # are equivalent to the following single rule. allow {user_t staff_t} self : process signal;


Notice that when using self, the equivalent rules are created only for each source type and themselves. In particular, user_t is given no access to staff_t and vice versa.

Note

You may use only the special type self in the target field of AV rules. In particular, you cannot use self as the source of an AV rule or in a type rule. Further, you cannot declare a type or attribute with self as its identifier.


The use of self is particularly valuable when using attributes or large lists of types and attributes as the source of an AV rule. For example, suppose we want every domain to be able to signal itself. We might want to write a rule such as this:

allow domain domain : process signal; # Not what we really want


Although this rule provides the desired access (every domain type would be able to signal itself), it would also allow every domain type to signal every other domain type. This unintended effect could be a security disaster. By using the self keyword, we can ensure that each domain type only gets access to itself, as follows:

allow domain self : process signal; # This is what we intended


5.3.1.5. The Negation Special Operator

The final syntax for types in AV rules is type negation. This syntax is useful for removing a type from a list of types and is most commonly used to remove a type from an attribute in a given rule. This is done by prepending the negation operator, -, to the beginning of the type name. For example, we could allow all domain types to execute all file types with the exec_type attribute except for sbin_t with the following rule:

allow domain { exec_type -sbin_t } : file execute;


This rule would expand as if the exec_type attribute did not contain the type sbin_t for this one rule.

Type negation is not order dependent; if a type is subtracted, it will not be expanded even if it comes before the attribute was listed. The following, for example, is semantically equivalent to the preceding example:

allow domain { -sbin_t exec_type } : file execute;


5.3.1.6. Specifying Object Classes and Permissions in AV Rules

AV rules can also contain lists of object classes and permissions. The syntax is, as with types, a space-separated list enclosed in braces, as follows:

allow user_t bin_t : { file dir } { read getattr };


This rule would result in two keys, one for each object class, just as with source or target types. This preceding rule is exactly equivalent to the following two rules:

# These two rules... allow user_t bin_t : file { read getattr }; allow user_t bin_t : dir { read getattr }; # are equivalent to the following single rule. allow user_t bin_t : { file dir } { read getattr };


Notice that the object classes are expanded, but each rule has the same list of permissions. This means that all the listed permissions must be valid for all the object classes. We will sometimes have to create two distinct rules with the same source and target types but different object classes because the permission lists are not valid for all classes. For example, if we look at the permissions for file and dir object class, we will notice that many of them are the same, but some are not. (The permissions valid for both are a result of the use of common permissions, as discussed in Chapter 4, "Object Classes and Permissions.")

Suppose, for example, we want to write a rule to give a form of "read" access for both object classes. The following rule is not valid:

# An invalid rule because search is not valid for the object class file allow user_t bin_t : { file dir } { read getattr search };


Although read and getattr are common permissions for both dir and file object classes, the search permission is valid only for dir object class. Because checkpolicy cannot create a key that gives file class an invalid permission (search), we would get an error when trying to compile a policy with this rule. Our only recourse in this case is to create two rules, such as these:

# Two rules are needed when permissions are not valid for # both object classes allow user_t bin_t : file { read getattr }; allow user_t bin_t : dir { read getattr search } ;


5.3.1.7. Special Permission Operators for AV Rules

We can use two special operators for listing permissions in AV rules. The first special operator is a wildcard operator (*). The wildcard operator includes all permissions for an object class:

allow user_t bin_t : { file dir } *;


This rule will expand into all of the permissions for file and dir.

The wildcard operator syntax differs subtly from explicitly listing all the permissions for the object classes. With the wildcard operator, all permissions are included for each object class individually, regardless of whether they are valid for all the object classes. This makes it possible to use the wildcard operator in rules with multiple object classes, even if those object classes have different permissions. So, for example, the above rule would safely handle the permissions that are only valid for dir object class and not file class, unlike the earlier example.

The second special operator makes it possible to include all the permissions not listed using the complement operator (~):

allow user_t bin_t : file ~{ write setattr ioctl };


When compiled, this rule allows all the permissions for the file object class except write, setattr, and ioctl. Similar to the wildcard operator, complement expands the permission lists individually for each listed object class.

Warning

Be advised that the proper and allowed use of all three special operators (negation, wildcard, and complement) has evolved and changed over the past few years. Many recent versions of checkpolicy will allow these operators to be used in places other than those listed here. For example, checkpolicy versions, including that released with RHEL4, allow the wildcard operator (*) to be used for types.

Recent improvements to the compiler have tightened the allowed use for these operators to be consistent with the rules previously discussed. The primary exception is that the wildcard operator may be used for types in neverallow rules, but no other TE rule. In general, if you use these operators as discussed herein, you will be safe.


Common Access Vector Rule Syntax

The full common syntax for AV rules is as follows:

rule_name  type_set  type_set : class_set  perm_set ;


rule_name

The name of the access vector rule. Valid rule names are allow, auditallow, auditdeny, dontaudit, or neverallow.

type_set

One or more types and/or attributes. There is a separate type_set for the source and target types of the rules. Multiple types and attributes are specified using a space-separated list enclosed in braces ({ })for example, { bin_t sbin_t }. Types can be excluded from the list by prepending - to the type name (for example, { exec_type -sbin_t }). The keyword self can be used in the target type field either alone or as part of a list of types and attributes. Self cannot be used in the source type field. Neverallow rules also support the wildcard operator (*) to include all types and the complement operator (~) to include all types except those explicitly listed.

class_set

One or more object classes. Multiple object classes must be enclosed in braces ({ })for example, { file lnk_file }.

perm_set

One or more permission. All permissions must be valid for all object classes in the class_set. Multiple permissions must be enclosed in braces ({ })for example, {read create}. The wildcard operator (*) is used to specify all permissions for all object classes. The complement operator (~) is used to specify all permissions except those explicitly listed.


All AV rules are valid in monolithic policies, base loadable modules, and nonbase loadable modules. All AV rules except auditdeny and neverallow rules are valid in conditional statements.


5.3.2. Allow Rules

By now you have seen many examples of allow rules in this and previous chapters. The allow rule is the most common rule in a policy and implements the primary purpose of an SELinux policy (that is, to allow access).

As discussed, we use allow rules to specify all permissions that will be granted at runtime. They are the only means to allow permissions in an SELinux policy. Remember, no access is allowed by default. We specify access between two lists of types, the source and target, in terms of permissions for the listed object classes, as follows:

allow user_t bin_t : file { read execute };


This rule allows any process whose security context has the type user_t to have read and execute permissions to any ordinary file whose security context has the type bin_t. Allow rules share all of the common AV rules syntax and do not have any additional syntax.

If this example were the only allow rule in our policy with this source type, target type, and object class, no other access would be granted to files with the type of bin_t. For example, user_t would not be able to write files of type bin_t.

Allow rules, like all AV rules, are cumulative and the actual access allowed for a given subject-target-class key is the union of all the allow rules that refer to that key. For example, these two sets of rules are equivalent:

# These two rules... allow user_t bin_t : file read; allow user_t bin_t : file write; # are equivalent to (and redundant with) this single rule. allow user_t bin_t : file { read write );


5.3.3. Audit Rules

SELinux has extensive facilities for logging, or auditing, access attempts that are either allowed or denied by the policy. The audit messages, often called "AVC messages," give detailed information about an access attempt, including whether it was allowed or denied, the security context of the source and target, and other details about the resources involved in the access attempt. The messages, which are similar to other kernel messages and are usually stored in log files under /var/log, are an indispensable tool for policy development, system administration, and system monitoring. In this chapter, we examine the policy features that enable us to configure which access attempts will generate audit messages. Part III provides more information about how to use audit messages to debug and understand policies.

By default, SELinux does not record any access checks that are allowed but records all access checks that are denied. These defaults are not surprising; on most systems, thousands of accesses per second are allowed, but few accesses are denied. The allowed accesses are, by the fact that they were allowed, expected and usually do not require auditing. The denied accesses are usually, but not always, unexpected, and auditing them helps an administrator to monitor for policy bugs and/or possible intrusion attempts. The policy language allows us to override portions of these defaults to suppress audit messages for expected access denials and to generate audit messages for access attempts that were allowed.

SELinux provides two AV rules that allow us to control which access attempts are audited: dontaudit and auditallow. These two rules are the policy mechanism that enable us to change these auditing defaults. The dontaudit rule is the most commonly used. It specifies which access denials should not be audited, overriding the SELinux default behavior to audit all access denials.

Warning

Access denials are audited only if the denial was made by SELinux. Recall from Chapter 3 that LSM module hook functions are usually called only if the access passes the standard Linux discretionary access control checks. This means that if an access was denied because of the standard Linux access checks, SELinux is not even aware of the access attempt and cannot generate an audit message. If you need to audit all denied accesses regardless of why the access is denied, you must directly use the kernel audit system included in the 2.6.x series of kernels. See the man pages for auditd(8) and auditctl(8).


For example, consider this:

dontaudit httpd_t etc_t : dir search;


This rule specifies that when processes of type httpd_t are denied search permission on directories of type etc_t, the denial should not be audited, overriding the default behavior. We might write this rule if processes with type httpd_t attempt to search directories of type etc_t (presumably /etc/) but function properly when this access is not granted. You will find Linux/UNIX applications often exhibit this type of behavior; that is, they attempt access they do not need yet work fine when the access is denied.

The dontaudit rule is useful when we want to mask audit denial messages that are expected, usually due to expected behavior of an application. The dontaudit rule allows us to avoid granting unnecessary access (because the application works without the access, it is unnecessary by any definition) without a large number of expected audit messages filling the system logs. As we said, this type of behavior is all too common.

Auditdeny Rule

Earlier versions of SELinux supported an auditdeny rule. These rules were used for a similar purpose to the dontaudit rules. Although still supported by the policy language, an auditdeny rule is seldom, if ever, seen in policies. The rule is deprecated, and we suggest you do not attempt to use it. The dontaudit rule, coupled with the default behavior of recording all access denials, is the desired method for controlling access denial auditing.


The other audit rule, auditallow, allows us to control the auditing of allowed access attempts. Unlike denied access, allowed access is not recorded by default. For example, let's look at the following rule:

auditallow domain shadow_t : file write;


This rules specifies that when a process with a type that has the domain attribute successfully obtains write access to files of type shadow_t, the allowed access is audited. The auditallow rule is useful to audit accesses that are an important security event. Examples of access that are likely to have an auditallow rule include writing to the shadow password file (as the above rule does) or reloading a new policy into the kernel.

Remember, audit rules let us override the default auditing settings. The allow rule specifies which access is allowed. The auditallow rule does not allow access; it enables only auditing of allowed permissions.

Note

Auditing is different in permissive and enforcing modes. When running in enforcing mode, audit messages are generated every time there is an allowed or denied access that the policy states should be audited up to a rate limit (this can be set with auditctl(8)). In permissive mode, only the first access attempt is logged until the next policy load or toggle of the enforcing mode. Permissive mode is most often used for policy development, and this auditing mode helps reduce the size of the log.


5.3.4. Neverallow Rules

The final AV rule is the neverallow rule. We use this rule to state invariant properties specifying certain accesses that may never be permitted by an allow rule. You might wonder why this rule exists, because access is denied by default. The reason is to aid policy writing by noting certain undesired permissions, thereby preventing the accidental inclusion of these permissions in our policy. Recall that an SELinux policy is likely to contain tens of thousands of rules. It is quite possible to accidentally grant an access we did not want to grant. The neverallow rule helps prevent this situation. For example, consider this rule:

neverallow user_t shadow_t : file write;


This neverallow rule would prevent us from adding a rule to the policy that allows user_t to write to files of type shadow_t by generating a compile error. This rule does not remove access, it just generates compile errors. The neverallow rule is to state important properties about our policy before we start writing allow rules. The neverallow rules prevent us from inadvertently including permissions that we did not intend.

The neverallow rule supports some additional syntax that the other AV rules do not. In particular, the source and target type lists in neverallow rules can contain the wildcard (*) and complement (~) operators. These operators work just as they do for permission lists in the rest of the AV rules (see the section "Special Permission Operators for AV Rules," earlier in this chapter).

For example, look at the following rule:

neverallow * domain : dir ~{ read getattr };


This rule states that no allow rule may grant any type any access except read and getattr access (that is, "read access") to directories labeled with one of the types associated with the domain attributes. The wildcard operator in this rule means all types. A neverallow rule similar to this is commonly found in policies and is used to prevent inappropriate access to directories in /proc/ that store process information (which will be labeled with the same type as processes).

We can see from the preceding example that the wildcard operator is needed in the source type lists for neverallow rules because we are referring to any and all types, including those not yet created. The wildcard operator allows us to prevent future mistakes.

Another common neverallow rule is this:

neverallow domain ~domain : process transition;


This neverallow rule reinforces the concept of the domain attribute described earlier in this chapter. This rule states that a process cannot transition to a type that does not have the domain attribute. This makes it impossible to create a valid policy with a type intended for a process that does not have the domain attribute.

Loadable Module Dependency Handling

Loadable policy modules, which are a new feature in Fedora Core 5 (FC5), contain language features for handling dependencies between modules. The dependency handling features ensure that the policy components (that is, identifiers) a module expects are present at module installation time. See Chapter 13, "Managing an SELinux System," for more information about how loadable policy modules are installed and managed. Possible policy component dependencies include object classes, permissions, users, roles, types or aliases, attributes, and Boolean identifiers.

The require statement states the policy components required for a loadable module. All policy components that are not declared in the module must be required in some form. For example, consider the following require statement:

require { type etc_t; }


The example above states that the loadable module in which it appears requires the type etc_t to be declared elsewhere in the policy (that is, in the base module or other loadable modules). This require statement allows the type etc_t to appear in policy rules within the module without being explicitly declared. Following is a more complete example showing a more require statement, type declaration, and an example allow rule:

require {        attribute domain;        type etc_t;        class file { read getattr }; } type httpd_t, domain; allow httpd_t etc_t : file { read getattr };


As you can see, every policy component used in the example allow rule was either declared or required before it was used. For example, the domain attribute was required before it was used in the httpd_t type declaration. Obviously, many require statements would be needed for a loadable module of any complexity. In Chapter 12, "Reference Policy," we discuss how the reference policy automates the generation of require statements.

We use the require statement to state unconditional requirements that must be present in the policy for the loadable module to be installed. The optional statement is used to state requirements that may or may not be present. This allows the policy author to add rules based on whether policy components are present. For example, consider the following optional statement:

optional {           require { type user_home_t; }           allow httpd_t user_home_t : file read; }


This statement allows processes with the type httpd_t to read files with the type user_home_t if that type is present. As you can see, the optional statement wraps standard policy statements, including require statements. Whenever modules are added or removed from the system, all the optional dependencies are checked and enabled or disabled as appropriate.

The full syntax of the require statement is as follows:

require { require_list }


require_list

One or more semicolon-separated require declarations. A require declaration consists of an identifier for the variety of policy component followed by the name of the policy component. Valid policy component variety identifiers are class, user, role, type, attribute, and bool. For users, roles, types, attributes, and Booleans, only a single name may be listed (for example, type httpd_t;). For object classes, both the object classes and one or more permissions is listed (for example, class file { read write };).


Require statements not a part of an optional statement are valid only in nonbase loadable modules. They are not valid in a base module or in any conditional statements.

The full syntax for the optional statement is as follows:

optional { rule_list }


rule_list

One or more policy statements that are enabled if all the required statements in the optional statement are satisfied. Valid policy statements are user, role, type, attribute, and alias declarations and TE and RBAC rules (including conditional statements).


Optional statements are valid only in base and non-base loadable policy modules. They are not valid in conditional statements.





SELinux by Example(c) Using Security Enhanced Linux
SELinux by Example: Using Security Enhanced Linux
ISBN: 0131963694
EAN: 2147483647
Year: 2007
Pages: 154

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net