7.2. Constrain StatementThe constrain statement has three elements: a set of object classes to which the constraint applies, a set of permissions for those classes that are being constrained, and a Boolean expression of the constraint. Constraints are organized and stored within the policy by object class. You can see the full syntax for the constrain statement in the sidebar on page 152.
The constrain statement lets you express constraints on any combination of the three elements of a security context (user, role, and type). Constraint expressions compare the contexts of the source (subject) process and the target (object) with each other and/or with explicit names (such as type or role identifiers). Constraint expressions can be complex, but in practice are usually small and specifically targeted. Here is an example constraint: constrain process transition (u1 == u2) ; Let's take a closer look at this constraint. First, note that it applies to the process object class only, and only constrains the transition permission for processes. Recall that the transition permission is required to allow a domain transition; in effect, this constraint further restricts domain transitions. Now let's look at the constraint expression (u1 == u2). The keywords u1 and u2 indicate, respectively, the source and target user identifiers for the security contexts. So, this expression resolves to true when the source and target user identifiers are the same. In the case of domain transitions, the source is the "current" security context, and the target is the "new" security context for the process. Looking at the preceding constraint in its entirety, we see that it requires that the source and target user identifiers remain the same for all domain transitions. How? Recall the description of the access algorithm earlier. When a process requests transition permission, and the AVC calls the security server to determine allowed access for the triple source-target-class, the preceding constraint would become effective (for the process object class) and would check the user identifier in the source and target security contexts. If the user identifiers are not the same, the bit in the mask indicating transition permission is removed before the granted access mask is returned to the AVC. Let's look at another example: constrain process transition (r1 == r2) ; This constraint is similar to the previous statement except that it constrains role identifiers rather than user identifiers. The keywords r1 and r2 indicate source and target role identifiers, respectively. This constraint requires that role identifiers not change on a domain transition in much the same way that the previous constraint requires user identifiers not to change. Because these two constraints relate to the same object class and permission, the constrain expression syntax allows us to combine them into a single Boolean expression: constrain process transition (u1 == u2 and r1 == r2) ; This single statement is equivalent to the two previous statements. Either form will have the same effect of restricting user and role identifier changes relating to domain transitions. Let's take our example a little further. In some situations, we want to allow the user and/or role identifier to change on a domain transition. For example, the login process needs to change the user and role identifiers to those of the user logging in. Another example is a program that allows you to change your role, which must be able to change the role identifier during a domain transition. In general, such programs are trusted processes, and we need a way to allow them to change user/role identifiers while ensuring that the constraint is active for all other programs. To achieve this goal, let's first define a way to recognize those domain types that are trusted to change user and role identifiers. We do this via a type attribute. In particular, let's assume that there are two attributes defined in the policy: privuser and privrole. The former is associated with all types allowed to change user identifiers, and the latter with those allowed to change role identifiers. With these attributes, we can change our constraint as follows: constrain process transition (u1 == u2 or t1 == privuser) ; constrain process transition (r1 == r2 or t1 == privrole) ; In both statements, t1 refers to the source type (just as t2, if used, refers to the target type). The first statement allows the user identifier to be changed in a domain transition only if the source type has the privuser attribute. Likewise, the role can be changed if the source type has the privrole attribute. Let's make sure you understand how attributes affect these constraints. Recall that the kernel expands attributes into the list of types that contain the attribute. So, to the kernel, the constraint really is a list of types rather than a single attribute. In the case of a list of types (or an attribute) on the right side of the operator, the == operator really means "is a member of" the set of types listed. Likewise the != means "is not a member of" the set of types. So, in our preceding example, the partial statement t1 == privuser really means "if the source type is in the list of types that have the privuser attribute." Note For constraint expressions, the left side of all operators must be one of the allowed keywords (for example, u1 or u2) and may never be a type, attribute, role, or user identifier (or list of identifiers). The right side of an operator may be a key word or one or more identifier names. If the left and right sides of the operator are the role keywords r1 and r2, you have a few more role operators to choose from; specifically eq, dom, domby, and incomp, although these are rarely used. Table 7-1 summarizes the operators allowed for expressions in constrain statements.
|