Doing away with interfaces


Sven Vermeulen Sat 29 August 2015

CIL is SELinux' Common Intermediate Language, which brings on a whole new set of possibilities with policy development. I hardly know CIL but am (slowly) learning. Of course, the best way to learn is to try and do lots of things with it, but real-life work and time-to-market for now forces me to stick with the M4-based refpolicy one.

Still, I do try out some things here and there, and one of the things I wanted to look into was how CIL policies would deal with interfaces.

Recap on interfaces

With the M4 based reference policy, interfaces are M4 macros that expand into the standard SELinux rules. They are used by the reference policy to provide a way to isolate module-specific code and to have "public" calls.

Policy modules are not allowed (by convention) to call types or domains that are not defined by the same module. If they want to interact with those modules, then they need to call the interface(s):

# module "ntp"
# domtrans: when executing an ntpd_exec_t binary, the resulting process 
#           runs in ntpd_t
  domtrans_pattern($1, ntpd_exec_t, ntpd_t)

# module "hal"

In the above example, the purpose is to have hald_t be able to execute binaries labeled as ntpd_exec_t and have the resulting process run as the ntpd_t domain.

The following would not be allowed inside the hal module:

domtrans_pattern(hald_t, ntpd_exec_t, ntpd_t)

This would imply that both hald_t, ntpd_exec_t and ntpd_t are defined by the same module, which is not the case.

Interfaces in CIL

It seems that CIL will not use interface files. Perhaps some convention surrounding it will be created - to know this, we'll have to wait until a "cilrefpolicy" is created. However, functionally, this is no longer necessary.

Consider the myhttp_client_packet_t declaration from a previous post. In it, we wanted to allow mozilla_t to send and receive these packets. The example didn't use an interface-like construction for this, so let's see how this would be dealt with.

First, the module is slightly adjusted to create a macro called myhttp_sendrecv_client_packet:

(macro myhttp_sendrecv_client_packet ((type domain))
  (typeattributeset cil_gen_require domain)
  (allow domain myhttp_client_packet_t (packet (send recv)))

Another module would then call this:

(call myhttp_sendrecv_client_packet (mozilla_t))

That's it. When the policy modules are both loaded, then the mozilla_t domain is able to send and receive myhttp_client_packet_t labeled packets.

There's more: namespaces

But it doesn't end there. Whereas the reference policy had a single namespace for the interfaces, CIL is able to use namespaces. It allows to create an almost object-like approach for policy development.

The above myhttp_client_packet_t definition could be written as follows:

(block myhttp
  ; MyHTTP client packet
  (type client_packet_t)
  (roletype object_r client_packet_t)
  (typeattributeset client_packet_type (client_packet_t))
  (typeattributeset packet_type (client_packet_t))

  (macro sendrecv_client_packet ((type domain))
    (typeattributeset cil_gen_require domain)
    (allow domain client_packet_t (packet (send recv)))

The other module looks as follows:

(block mozilla
  (typeattributeset cil_gen_require mozilla_t)
  (call myhttp.sendrecv_client_packet (mozilla_t))

The result is similar, but not fully the same. The packet is no longer called myhttp_client_packet_t but myhttp.client_packet_t. In other words, a period (.) is used to separate the object name (myhttp) and the object/type (client_packet_t) as well as interface/macro (sendrecv_client_packet):

~$ sesearch -s mozilla_t -c packet -p send -Ad
  allow mozilla_t myhttp.client_packet_t : packet { send recv };

And it looks that namespace support goes even further than that, but I still need to learn more about it first.

Still, I find this a good evolution. With CIL interfaces are no longer separate from the module definition: everything is inside the CIL file. I secretly hope that tools such as seinfo would support querying macros as well.