Section 9.3. Conditional Statements


9.3. Conditional Statements

The reason we have Boolean variables in an SELinux policy is to allow us to write rules that are conditionally enabled using the conditional statement (if). The conditional statement has a conditional expression that is formed using Booleans and a true and optional false list of rules. If the conditional expression resolves to true, the true list of rules is enabled, and the false list of rules is disabled. If the conditional expression resolves to false, the opposite case prevails. We can change the value of a conditional expression on a running system by changing the current values of the Boolean variables the expression uses.

9.3.1. Conditional Expressions and Rule Lists

The simplest and most common form of a conditional statement has a single Boolean variable as its conditional expression and a true list (but no false list) of rules. For example, continuing with our ping example, we can write rules that allow user domains to use ping when the Boolean user_ping is enabled with a conditional statement similar to the following:

# Example: controlling user ping via a Boolean #   Assumptions (defined elsewhere in policy): #      unpriv_userdomain: attribute for all ordinary user domains #      ping_t: domain type for the ping process (which has necessary #              network interface access for ping to work) #      ping_exec_t: entrypoint file type of the ping executable if ( user_ping ) {     # domain transition access to allow user access     allow unpriv_userdomain ping_t : process transition;     allow unpriv_userdomain ping_exec_t : file { read getattr execute };     # entrypoint might be redundant since ping_t should already have it     # but adding it again is not harmful     allow ping_t ping_exec_t : file entrypoint;     # cause the transition to happen by default     type_transition unpriv_userdomain ping_exec_t: process ping_t; }


In this example, we see that all we have to do to enable access is give all ordinary user domain types (all of which are assumed to be associated with the unpriv_userdomain attribute elsewhere in the policy) domain transition access to the ping program domain type (ping_t). Elsewhere in the policy, we would write the rules that provide the ping domain type the network access necessary for ping to work. In the conditional policy, all we have to do is control the ability of user domain type to transition to ping_t. We also need to ensure that the user's role is also authorized for the ping_t domain type (see Chapter 6, "Roles and Users"). In this case, it is typical to unconditionally authorized the ping_t domain type for the intended user role, and control whether this authorization can be utilized via type transition permission as we illustrated previously.

Conditional (if) Statement Syntax

The conditional statement (if) specifies policy statements that are enabled/disabled (that is, enforced or not enforced by the kernel) depending on the value of a conditional expression. The full syntax of the conditional statement is as follows:

if (cond_expression) { true_list } [  else { false_list } ]


cond_expression

A conditional expression made up of one or more Boolean variables with logical operators. Supported logical operators are listed in Table 9-1. Boolean variables must be defined using the bool statement.

TRue_list, false_list

A list of rules that are conditionally enabled or disabled depending on the value of the conditional expression. When the conditional list is true, the true list of rules is enabled (and the false disabled). When false, the opposite is the case. The false list is optional. The kernel will enforce only conditional rules that are enabled. The supported rules for these lists are allow, auditallow, dontaudit, type_transition, and type_change.


The conditional statement is valid in monolithic policies, base loadable modules, and non-base loadable modules.

At the time of this writing, conditional expressions may not be nested.


Let's look at another example that uses both the true and false list of rules. Suppose that we want to control the ping_t domain such that the docked Boolean introduced earlier determines what access the ping program has (that is, access to wireless Ethernet devices only when "not docked"). The following policy statements are a partial solution to this objective:

# Example: restricting ping's access based on docked state #   Assumptions (defined elsewhere in policy): #      docked: Boolean indicating docked state #      ping_t: domain type for the ping process #      wired_netif: attrib for all wired netif types #      wireless_netif: attrib for all wireless netif types # Allowed wired access when docked, wireless otherwise if ( docked ) {     allow ping_t wired_netif:netif { tcp_send tcp_recv udp_send               udp_recv rawip_send rawip_recv }; } else {     allow ping_t wireless_netif:netif { tcp_send tcp_recv udp_send               udp_recv rawip_send rawip_recv }; } # Remaining network and other access needed regardless of interface allow ping_t self:capability { net_raw setuid }; # etc., remaining rules not listed for simplicity


In this example, we control access to the two kinds of network interfaces, including raw access (rawip_send and rawip_recv), using the conditional statement. We provide other access needed by ping regardless of network interface using unconditional rules (that is, rules not within a conditional statement, which are always enabled regardless of the value of any Boolean).

Warning

In SELinux, type enforcement (TE) rules are always additive; that is, they always add permissions for a source-target-class triple. There is no way to remove permissions from a policy using conditional statements. Because no permissions are allowed by default, this means that you must be careful when writing allow rules not to add a permission in one place in the policy and then try to make it conditional in another. The unconditional rules (that is, those not within a conditional statement) will always take precedence. Therefore, if you try to control a permission in a conditional rule that is already allowed by an unconditional rule, the conditional rule will have no effect. Whereas the conditional rule will be enabled/disabled according to the conditional expressions, the nonconditional rule will always allow the permission.


Finally, let's examine one additional example where we use a more complex conditional expression. Suppose we want to expand the notion of user_ping above to control whether ping is allowed for any user domain and not just for ordinary user domains. So, instead of a Boolean named user_ping, we will use a Boolean named allow_ping to better represent our intent. Further, we want ping accessible only when the computer is docked. Therefore, we create the following partial solution:

# Example: restricting ping based on docked state and allow Boolean #   Assumptions (defined elsewhere in policy): #      docked: Boolean indicating docked state #      allow_ping: Boolean indicating whether ping is allowed #      ping_t: domain type for the ping process #      ping_exec_t: entrypoint file type of the ping executable #      wired_netif: attrib for all wired netif types #      userdomain: attrib for all user domains (priv & unpriv) # Allowed wired access when docked if allowed if ( allow_ping && docked ) {     # domain transition permission     allow userdomain ping_t : process transition;     allow userdomain ping_exec_t : file { read getattr execute };     allow ping_t ping_exec_t : file entrypoint;     type_transition userdomain ping_exec_t: process ping_t;     # wired netif access for ping     allow ping_t wired_netif:netif { tcp_send tcp_recv udp_send               udp_recv rawip_send rawip_recv }; }


This example shows the use of a two Booleans and a logical operator (&&) in a conditional expression. In this case, the values of both Booleans control whether this condition is true and the associated allow rules are enabled. Conditional expressions support a common set of C-like logical operators and typical parentheses rules for precedence. The logical operators supported for conditional expressions are listed in Table 9-1.

Table 9-1. Supported Operators for Conditional Expressions

Operator

Syntax

Semantic

&&

bool_1 && bool_2

Logical and

||

bool_1 || bool_2

Logical or

^

bool_1 ^ bool_2

Logical exclusive or

!

!bool_1

Logical not

==

bool_1 == bool_2

Are equivalent

!=

bool_1 != bool_2

Are not equivalent


9.3.2. Conditional Statement Limitations

The conditional policy language extensions have several significant limitations that were known at the time the extensions were developed. In reality, these limitations have not, as yet, resulted in any practical limitations. Nonetheless, you should be aware of the limitations. Some of these limitations will likely be removed or improved as greater use of conditional policy comes into being.

9.3.2.1. Supported Statements

As of now, the only policy statements allowed within a true or false list of a conditional are the following:

allow (type allow rules, and not role allow rules)

auditallow dontaudit type_transition type_change


These are the TE policy rule statements. The reason for this limitation is that conditional policies were really developed to support conditional TE policies. Therefore, the TE rules were supported. This makes sense because what we are talking about is enabling and disabling rules that allow access, audit access, and setup access defaults.

In particular, we do not allow you to define types or other policy identifiers within a conditional expression. It is difficult to imagine good policy design where policy components are defined based on runtime conditions (as controlled by Booleans). Instead, policy component identifiers such as types are either defined or not in a given policy. They are not "conditional." This reasoning also explains why user and role declarations, and indeed Boolean declarations, are not supported within conditional statements.

Some unsupported statements would be valuable within conditional statements. For example, we are finding that the typeattribute statement, which associates a previously defined attribute with a previously defined type, is a useful statement to allow within conditional true/false list. Because adding an attribute to a type is essentially adding rules that allow access to/from that type, it makes sense that one might want to include this in a runtime conditional. The reason why the typeattribute statement was not supported in the initial conditional policy implementation is simply that the typeattribute statement itself was not supported in the policy language at the time. We expect this and other statements will eventually be supported by the policy compiler.

Warning

Do not confuse policy build-time options with runtime conditionals. It is common to use a scripting/macro language (for example, m4) to provide build-time options (see Chapters 11, "Original Example Policy," and 12, "Reference Policy"). For example, you can control whether certain domain types and their associated rules are included in a given policy (for example, because our intended system does not use the programs for which those domain types were designed). In this case, we would exclude all the rules and the associated type declarations from the compiled policy. This is a compile-time customization, which is entirely different from a runtime conditional. In the latter case, we include all the rules and types we want, but allow some rules (but not types) to be toggled on/off based on conditions as controlled by Booleans.


9.3.2.2. Nesting Conditional Statements

Currently, the conditional statement syntax does not support nesting. So, for example, the following policy statements would cause a compiler error:

# These statements would currently fail due to nonsupport for nesting if (docked) {       # docked statements       if (allow_ping) {             # docked and allow statements       } } else {     # undocked statements }


Instead, we would have to write this as more verbose separate statements, such as the following:

# This workaround with no nesting works! if (docked && allow_ping) {       # docked and allow statements } if (docked) {       # docked statements } else {       # undocked statements }


We expect support for nesting conditional statements to be added to the language soon, possibly by the time this book is published.




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