14.3. Creating an Initial Policy ModuleIn the next steps, we create an initial policy based on the information we gathered and the security goals we specified. To create the most secure policy module possible, we want to create a policy that grants only the access that we expect the IRC daemon to require before testing begins. 14.3.1. Creating Policy Module FilesWe begin our policy module development by creating all the policy module files for both the example and reference policies. 14.3.1.1. Example PolicyAs we discussed in Chapter 11, a policy module in the example policy consists of two files: the policy rules file (.te) and the file context files (.fc). Therefore, for the IRC daemon policy module we need to create the files domains/programs/ircd.te and file_contexts/programs/ircd.fc. Initially, these files can be empty. Note All path names are relative to the root of the policy source directory. For the example policy this is /etc/selinux/strict/src/policy, and for the reference policy this is /etc/selinux/refpolicy/src/policy. We also refer to just the filenames (for example ircd.te, meaning /etc/selinux/strict/src/policy/domains/programs/ircd.te for the example policy). 14.3.1.2. Reference PolicyAs discussed in Chapter 12, a reference policy module consists of three files: the private policy file (.te), an external interface file (.if), and the labeling policy file (.fc). Because the IRC daemon is a system service, we put its policy module files in the services layer (that is, policy/modules/services/ircd.te, policy/modules/services/ircd.if, and policy/modules/services/ircd.fc). The files ircd.if and ircd.fc can be empty initially, but the file ircd.te must minimally declare the module as follows: # Ircd policy module declaration policy_module(ircd, 1.0) 14.3.2. Declaring TypesThe next step is to declare the appropriate domain and object types for our policy module. Remember, access can be allowed only between types, so we must identify and declare the correct set of types to represent our application architecture. In many ways, this is the most important step in policy module development. If we do not correctly identify the needed types, particularly domain types, the rest of the policy cannot be correct. Policy modules typically declare types for the following:
The types we declare for our IRC daemon policy module closely match the high-level architecture of target application. Our IRC types are as follows:
This is a representative set of types for a simple daemon such as IRC. Notice that other than the first two types, which are the domain type and the entrypoint type (ircd_t and ircd_exec_t), all these types are for application resources controlled by ircd. 14.3.2.1. Example PolicyRecall that in the example policy, types are declared directly (including the list of associated attributes) or through macros. Listing 14-2 shows our type declarations for the example policy IRC daemon policy module. We have directly declared all the types in our policy module instead of using macros to make the policy module clearer. Notice that each of these types has a variety of attributes. For example, the log file type ircd_log_t has the attributes file_type, sysadmfile, and logfile. We determined the needed attributes based on the intended use for each type (that is, ircd_log_t is intended as a type for a log file that can be accessed by system administrators) and the available attributes. Listing 14-2. Example Policy: IRC Daemon Type Declarations (ircd.te)
Tip Recall that the file attrib.te in the root directory of the example policy source contains all the attribute declarations and documentation about their use. 14.3.2.2. Reference PolicyIn the reference policy, types are always directly declared and do not include attributes. Listing 14-3 shows our type declarations for the IRC reference policy private policy module (ircd.te). Notice that each of the type declarations is paired with an interface call (to another, existing policy module) that is functionally equivalent to the attribute assignments in the example policy module in Listing 14-2. For instance, line 24 declares the type ircd_conf_t, and line 25 marks it as a configuration file by calling the interface files_config_file(). Determining which interface to call for each type declaration is similar to determining which attributes are required, although the reference policy has better documentation and is easier to understand and use. Listing 14-3. Reference Policy: IRC Daemon Private Type Declarations (ircd.te)
Tip Remember that the reference policy includes a significant amount of documentation generated from the source. The documentation is the best way to find appropriate interfaces like those used above in the type declarations. You can view the documentation at the reference policy Web site or locally after running the command make html in the reference policy source directory. In FC5, the HTML documentation is available under /usr/share/doc/selinux-policy-x.y.z/html/. 14.3.3. Allowing Initial Restrictive AccessThe next step is to grant permissions based on our best understanding of the initial, restrictive access needed for the IRC domain type (ircd_t). The access allowed should reflect both our security goals and the functional needs of the IRC daemon. In our experience, it is helpful to first plan the access required in an abstract way because writing raw SELinux policy rules requires significant attention to detail. By creating a higher-level plan first, it is easier to keep the larger security goals in mind. For example, we expect the ircd_t domain to have the following access consistent with our security goals:
Note We have chosen to give fairly broad network access initially. We are not, for example, restricting the network interfaces and hosts with which the IRC daemon can communicate. This is a common practice that removes the need to customize the policy based on local network settings and topology. It is possible (and often desirable), however, to add these restrictions if customizing the policy for each server because local adjustments are feasible. 14.3.3.1. Example PolicyIn the example policy, we allow access using a combination of direct allow rules and example policy macros. For example, consider Listing 14-4. (This policy section is added after the type declarations that were discussed in Listing 14-2.) Listing 14-4. Example Policy: IRC Daemon Initial Allowed Access (ircd.te)
Notice that each commented block of rules corresponds to one of the items in our list of initial accesses specified. To allow access between types declared in our module, we primarily use allow rules directly. For example, lines 1012 permit the domain type ircd_t to read configuration files (that is, files and directories with the type ircd_conf_t). There are exceptions, however, where policy rules between our IRC types are added to our policy through macros. For example, the file_type_auto_trans() macro on line 19 allows the domain type ircd_t to create, read, and write files with the type ircd_var_run_t (that is, /var/run/ircd.pid). Access to types declared outside of our policy module is also allowed using a combination of direct allow rules and macros. For example, line 42 allows the ircd_t domain to read and write character device files with the type null_device_t (that is, /dev/null) using an allow rule that directly references both types. This is an example of one of the example policy's biggest weakness of the example (that is, closely coupled policy modules). Because our IRC module must have explicit knowledge of types declared in other modules (null_device_t), the implementation of the two modules are intertwined. By contrast, the access required to use shared libraries is allowed entirely by the uses_shlib() macro, as shown on line 31. In the example policy, the choice of whether to use direct access or macros is primarily one of style and whether an appropriate macro is available. There are no strong conventions as in the reference policy. The network access for the IRC daemon is allowed through the can_network() macro. Unfortunately, this macro allows more access than our (or nearly any) application needs, although it has been improved from its original implementation. In particular, it allows sending and receiving raw and UDP packets in addition to TCP. We used the macro despite the additional access it allows to reflect common practice for the example policy. There is no convenient way, other than direct allow rules, to allow a smaller subset of network access and most policy modules use the can_network() macro to allow network access. Tip Most of the macros used in our policy module reflect common practice for the example policy. Reading existing policy modules is the best way to familiarize yourself with the common macros and how they are used. Reading the macros in the macros/*.te files is also helpful. 14.3.3.2. Reference PolicyAccess in the reference policy is allowed by a combination of allow rules and call to interfaces defined in other modules. Recall from Chapter 12 that access to any type not declared in our policy module is allowed only through an interface. So, unlike our example policy module, the IRC daemon private policy file will never reference types from other modules directly. Listing 14-5 is the reference policy version of our initial restrictive access for the IRC daemon (which would be in the ircd.te file following the rules in Listing 14-3). Listing 14-5. Reference Policy: IRC Daemon Private Allowed Access (ircd.te)
Again, each commented block represents where we have allowed all of the initial access listed. The choice of using direct allow rules versus interfaces in the reference policy follows a strong convention. It is more straightforward than the choice of using direct allow rules versus macros in the example policy because of the clear encapsulation of types in the reference policy. Notice that the interfaces used in the reference policy are clearer and more explicit than the macros in the example policy. The explicit nature of interfaces sometimes makes a reference policy module more verbose, as is the case in allowing the use of shared libraries on lines 41 and 42. However, this verbosity also allows for more choice and better granularity of access. For example, the network access that we allow in the reference policy version of our policy module exactly matches the initial restrictive access that we intended. This is possible because the network access is broken down into many interfaces, each allowing a small portion of the access, rather than one broad macro in the example policy (that is, the can_network() macro). 14.3.4. Allowing Domain Transitions and Authorizing RolesFor our new domain to be effective, we must permit other domains to transition to our new domain. To do this, we must create type_transition rules, allow the domain transition, and authorize our domain type for the appropriate roles. As a general practice, the number of domains that may transition to a daemon domain should be limited. The IRC daemon package that comes with FC4 includes init scripts to allow starting from init during boot or directly by the system administrator. To permit both of these startup methods, we must ensure our policy does the following:
Note Using a role transition rule to run the IRC daemon in the system_r role is not required. We could have authorized ircd_t for sysadm_r instead. The use of the role transition is standard practice for system_r, however, because it results in more similar security contexts regardless of whether the daemon as started by init or the system administrator. The tradeoff is that the user, presumably root, must be authorized for both roles. 14.3.4.1. Example PolicyWe accomplish the domain transitions and role authorizations in the example policy as shown in Listing 14-6. Listing 14-6. Example Policy: IRC Daemon Domain and Role Authorizations (ircd.te)
The domain_auto_trans() macro both allows the domain transition and adds the necessary type transition rule required for an automatic domain transition. 14.3.4.2. Reference PolicyWe have already accomplished this step in the reference policy by using the interface init_daemon_domain() on line 17 in Listing 14-3. This interface allows all the domain and role transitions described previously in a consistent, configurable manner. 14.3.5. Integrating into the System PolicyOur initial restrictive access is primarily concerned with allowing the access needed by the IRC daemon. We also have to allow other domain types access to the resource types in our policy module. For example, log files are useful only if an administrator tool can read them. This is what we mean by integrating into the system policy. Much of the more common additional access is handled automatically through attributes in the example policy and interfaces in the reference policy. For example, adding the file_type attribute (example policy) or calling the files_type() interface (reference policy) allows a variety of domains, including sysadm_t, to read files with the associated file type. We often need to allow module-specific access for certain types of policy resources that we can grant to other domains. To demonstrate, let's expand our modules to allow other domains to read files with the type ircd_log_t. 14.3.5.1. Example PolicyThere is no defined way to allow access from other policy modules in the example policy. The rules can simply be placed either in our policy module or in the other policy, with types from both policy modules being directly referenced. For instance, the policy statements in Listing 14-7 allow logrotate_t to read the IRC daemon log files. In an example policy, we could have just as easily put these rules in the logrotate module. Listing 14-7. Example Policy: IRC Daemon, Allowing Access for logratate Domain (ircd.te)
Notice that we wrap these rules in an m4 ifdef statement that prevents the inclusion of the rules if the logrotate policy module is not present during policy compilation. The challenge with this approach of course, is that it is difficult to know where all the rules for a given type are located in the policy. This is another example of one of the motivations for the improvements in reference policy (that is, strong modularity and encapsulation). 14.3.5.2. Reference PolicyAllowing access from other policy modules is more structured in the reference policy through the use of interfaces. Listing 14-8 shows the external interface file (ircd.if) for our IRC policy module that declares an interface for reading the IRC daemon log files. As discussed in Chapter 12, in the reference policy, the only way for other modules to access a private type is to use an interface. Listing 14-8. Reference Policy: IRC Daemon External Interface Example (ircd.if)
Allowing access by other domains is a simple matter of calling this interface in the other policy modules. For example, to allow logrotate to read the IRC log files, the following interface call would be added to the logrotate policy module: irc_read_log(logrotate_t) Note Notice that the interface file also includes the module summary documentation and summaries for each interface. This allows us to generate detailed interface documentation from reference policy source files. 14.3.6. Creating the Labeling PolicyThe next step, which completes our initial policy module, is to create and apply the labeling policy in the form of file contexts statements, as discussed in Chapter 10, "Object Labeling." The labeling policy assigns the types intended for filesystem objects to files and directories. We use the information we gathered about the location of files and directories installed with the IRC daemon to derive statements. 14.3.6.1. Example PolicyListing 14-9 shows the file context file (ircd.fc) for the example policy. Notice that this file is a straightforward, hard-coded listing of files and directories for the IRC daemon in a syntax understandable by the setfiles program (see Chapter 10). Listing 14-9. Example Policy: IRC Daemon File Contexts File (ircd.fc)
14.3.6.2. Reference PolicyListing 14-10 shows the labeling policy file (ircd.fc) for our reference policy module. Listing 14-10. Reference Policy: IRC Daemon Labeling Policy File (ircd.fc)
The reference policy ircd.fc is essentially identical to the equivalent file in the example policy, except for the use of the gen_context() template interface macro. This template interface allows the reference policy to transparently handle multilevel security / multicategory security (MLS/MCS) and non-MLS/MCS policies from the same policy source. All security contexts must be specified using gen_context() in the reference policy. 14.3.7. Applying the PolicyThe final step before testing is to compile, install, load, and apply the policy. This is done in the same way for both the example and reference policies. First, compile, install, and load the policy using the following commands: # make && make install && make load If this is successful you should not see any errors and the build system will show a successful policy load. For example, for the example policy, the end output for a successful compile will be similar to the following. The reference policy output will be different but equally obscure to the uninitiated. Building file contexts f1iles... /usr/bin/checkpolicy -o policy.20 policy.conf /usr/bin/checkpolicy: loading policy configuration from policy.conf /usr/bin/checkpolicy: policy configuration loaded /usr/bin/checkpolicy: writing binary representation (version 20) to policy.20 Compiling policy ... /usr/bin/checkpolicy -o /etc/selinux/strict/policy/policy.20 policy.conf /usr/bin/checkpolicy: loading policy configuration from policy.conf /usr/bin/checkpolicy: policy configuration loaded /usr/bin/checkpolicy: writing binary representation (version 20) to /etc/selinux/strict/policy/policy.20 /usr/bin/checkpolicy -c 19 -o /etc/selinux/strict/policy/policy.19 policy.conf /usr/bin/checkpolicy: loading policy configuration from policy.conf /usr/bin/checkpolicy: policy configuration loaded /usr/bin/checkpolicy: writing binary representation (version 19) to /etc/selinux/strict/policy/policy.19 install -m 644 tmp/system.users /etc/selinux/strict/users/system.users install -m 644 tmp/customizable_types /etc/selinux/strict/contexts/customizable_types install -m 644 tmp/port_types /etc/selinux/strict/contexts/port_types Installing file contexts files... install -m 644 file_contexts/homedir_template /etc/selinux/strict/contexts/files/homedir_template install -m 644 file_contexts/file_contexts /etc/selinux/strict/contexts/files/file_contexts Loading Policy ... /usr/sbin/load_policy /etc/selinux/strict/policy/policy.19 touch tmp/load In addition, the policy load can be seen in the audit log. For example, here is an audit message generated from a load policy event: Feb 13 23:07:48 kernel: audit(1139890068.158:15709654): avc: granted { load_policy } for pid=1173 comm="load_policy" scontext=root:sysadm_r:load_policy_t tcontext=system_u:object_r:security_ t tclass=security
After the policy is successfully installed and loaded, we can relabel the filesystem to ensure that our new file contexts file is effective. Again, this procedure is the same for the example and reference policy. Below, we use the restorecon command to relabel all the files and directories specified in the file context file for our module: # restorecon /usr/bin/ircd # restorecon -R /etc/ircd/ /var/log/ircd/ /var/lib/ircd/ We can verify that the labeling occurred correctly using the ls command (note you can also use ls -Z), as follows: # ls scontext /usr/bin/ircd /var/log/ircd/ system_u:object_r:ircd_exec_t /usr/bin/ircd /var/log/ircd/: system_u:object_r:ircd_log_t ircd.log Tip Labeling a filesystem with newly defined types can only occur after loading the new policy because the kernel must be aware of the new types. After these steps, our initial policy module for the IRC daemon is now complete and ready for testing. |