NetLabel Address Selectors

One of the biggest differences between NetLabel and the labeled networking mechanisms of existing Trusted OSs is how outbound traffic is selected for labeling. Ever since NetLabel was first introduced in kernel 2.6.19 the on-the-wire outbound labeling protocol was determined by the label of the sending application’s socket. Despite the departure from legacy approaches, this was a concious choice designed to make the implementation smaller and less invasive in an attempt to gain acceptance into the mainstream Linux Kernel. While ultimately this approach proved to be successful, NetLabel was accepted, it did have its drawbacks. The most significant was that all traffic from a single socket, or application if it was not label aware, was limited to the same on-the-wire label; if you think about this for a minute you quickly realize how limiting this could become. Thankfully with kernel 2.6.28 this is no longer the case.

In kernel 2.6.28 we introduced the concept of address selectors to NetLabel. NetLabel address selectors allow administrators to specify the on-the-wire label format based on both the sending application and the destination address. This is a huge usability boost as administrators are no longer forced to use the same on-the-wire label format for a single application, making deployment much easier. To help make things a bit more concrete, let’s examine the following configuration:

# netlabelctl cipsov4 add pass doi:16 tags:1
# netlabelctl map add domain:apache_t protocol:cipsov4,16
# netlabelctl -p map list
Configured NetLabel domain mappings (2)
 domain: "apache_t"
   protocol: CIPSOv4, DOI = 16
 domain: DEFAULT
   protocol: UNLABELED

Before kernel 2.6.28 this is how you used to enabled NetLabel based labeling for an application. In this particular example we are configuring Apache (apache_t) to label outbound traffic with a CIPSO label using CIPSO DOI 16. This configuration would apply CIPSO labeling to all traffic regardless of destination; DNS queries, Windows clients, other Linux hosts, everything would receive CIPSO labeled traffic. However, starting with Linux Kernel 2.6.28 and NetLabel Tools 0.19 there are some new parameters to the netlabelctl map command:

# netlabelctl cipsov4 add pass doi:17 tags:5
# netlabelctl map add domain:firefox_t address:0.0.0.0/0 protocol:unlbl
# netlabelctl map add domain:firefox_t address:10.0.0.0/8 protocol:cipsov4,17
# netlabelctl map add domain:firefox_t address:192.168.4.5 protocol:cipsov4,16
# netlabelctl -p map list
Configured NetLabel domain mappings (3)
 domain: "apache_t"
   protocol: CIPSOv4, DOI = 16
 domain: "firefox_t"
   address: 192.168.4.5/32
    protocol: CIPSOv4, DOI = 16
   address: 10.0.0.0/8
    protocol: CIPSOv4, DOI = 17
   address: 0.0.0.0/0
    protocol: UNLABELED
 domain: DEFAULT
   protocol: UNLABELED

Using the new address option to the netlabelctl map command allows us to add a destination address selector to the NetLabel LSM domain mapping configuration. In this particular example we’ve configured Firefox (firefox_t) to send CIPSO DOI 16 labeled packets to host 192.168.4.5 and CIPSO DOI 17 labeled packets to the entire 10.0.0.0/8 network while everyone else, specified by 0.0.0.0/0, is sent unlabeled packets. You will also notice that the new address selectors can coexist with the existing configuration, e.g. our Apache configuration is untouched, but you can only add address selectors to domains which make use of address selectors, e.g. you can’t add address selectors to the above Apache configuration. If you want to add address selectors to an existing domain configuration you first need to delete it and recreate it using address selectors as show below:

# netlabelctl map add domain:apache_t address:192.168.3.5 protocol:unlbl
netlabelctl: error, invalid argument or parameter
# netlabelctl map del domain:apache_t
# netlabelctl map add domain:apache_t address:0.0.0.0/0 protocol:cipsov4,16
# netlabelctl map add domain:apache_t address:192.168.3.5 protocol:unlbl
# netlabelctl -p map list
Configured NetLabel domain mappings (3)
 domain: "apache_t"
   address: 192.168.3.5/32
    protocol: UNLABELED
   address: 0.0.0.0/0
    protocol: CIPSOv4, DOI = 16
 domain: "firefox_t"
   address: 192.168.4.5/32
    protocol: CIPSOv4, DOI = 16
   address: 10.0.0.0/8
    protocol: CIPSOv4, DOI = 17
   address: 0.0.0.0/0
    protocol: UNLABELED
 domain: DEFAULT
   protocol: UNLABELED

That covers the new NetLabel address selectors in kernel 2.6.28, however there are a few things you should make note of before you start playing with this new feature. The most important is that the address selectors require NetLabel Tools version 0.19 or higher, information on where to get version 0.19 can be found here. The next thing to keep in mind is that when you are configuring NetLabel using address selectors you will almost always want to have a 0.0.0.0/0 address configured as a “catch all” address, otherwise you could run into problems if you try to send traffic to an address not explicitly configured. Other than that, have fun and let me know if you have any problems in the comments.

Network Ingress/Egress Controls

I’ve already talked about Fallback Labels and Network Peer Controls now I’m going to explain the last major labeled networking feature included in the 2.6.25 release of the Linux Kernel, the network ingress/egress controls. One of the longstanding problems with the SELinux network access controls was that they lacked any ability to control packets at the network interface level, limiting our ability to provide access control based on the physical network and making it impossible to provide access control for forwarded packets. The network ingress/egress controls were designed to solve these problems by placing SELinux network access controls at the network interface level.

The new ingress/egress controls are fairly simple: each packet entering the system must pass an ingress access control and each packet leaving the system must pass an egress access control. Forwarded packets must also pass an additional forwarding access control, but I’m going to leave that for another time and focus on the ingress/egress cases. In both the ingress and egress access controls the network packet is subjected to network interface and network address checks. In order to allow incoming network traffic to enter the system you need the following two allow rules (where network_if_t is the network interface’s label, network_addr_t is the remote host’s network address label and peer_t is the traffic’s peer label):

allow peer_t network_if_t:netif ingress;
allow peer_t network_addr_t:node recvfrom;

Network traffic leaving the system requires similar allow rules:

allow peer_t network_if_t:netif egress;
allow peer_t network_addr_t:node sendto;

As a reminder the peer labels, peer_t in the above examples, are derived from the original sender’s security label. In the case of incoming network traffic the peer label is taken from the network labeling protocol, if available, and in the case of outgoing network traffic the peer label is taken from the application/socket which generated the traffic. The network interface and address labels are defined as part of the SELinux policy and can be modified on-the-fly using the semanage tool.

Moving on to a more concrete example, imagine a web server running Apache with the eth0 network interface labeled as “wwwsrv_if_t”, no explicitly labeled network addresses (which means addresses will be represented by the default address label, “node_t”) and a “private_net_t” fallback label defined for addresses that match 192.168.0.0/16. If we want to allow network traffic from 192.168.0.0/16 over the eth0 interface into the system and allow responses back from Apache we need to write the following allow rules:

allow private_net_t wwwsrv_if_t:netif ingress;
allow private_net_t node_t:node recvfrom;
allow apache_t wwwsrv_if_t:netif egress;
allow apache_t node_t:node sendto;

Like the new network peer controls, the ingress/egress controls are currently not enabled, protected by the same policy capability flag which protect the peer controls, “network_peer_controls”. I am currently testing a small patch which should solve the remaining policy issues and allow us to finally enable this new functionality. In the meantime if you want to try it yourself you need to uncomment the “policycap network_peer_controls;” line in the policy_capabilities file found in the SELinux Reference Policy sources and rebuild the policy. Good luck, and if you hit any snags don’t hesitate to drop a comment below and I’ll help you sort it out.

Network Peer Controls

In addition to Fallback Labels another new feature added to the 2.6.25 release of the Linux Kernel was the consolidation of the access control permissions for the different peer labeling mechanisms supported by SELinux. This consolidation has several benefits including better handling of traffic with multiple peer labels (all of the labels must be the same), elimination of confusing and duplicated per-packet access checks, and the creation of a new object class which abstracts away the underlying labeling protocol making policy development easier and more consistent.

Prior to the new network peer controls each peer labeling mechanism, NetLabel and Labeled IPsec, had separate access controls. In the early days of NetLabel this made sense as it allowed NetLabel development to proceed without impacting the existing Labeled IPsec mechanism, however, as time has progressed the additional access controls have proven problematic both from a code and policy management point of view. Those familiar with the old system know that in order to receive network traffic labeled with either NetLabel or Labeled IPsec you needed the following two allow rules (where socket_t is the receiving socket’s label and peer_t is the packet’s peer label):

allow socket_t peer_t:{ tcp_socket udp_socket rawip_socket } recvfrom;
allow socket_t peer_t:association recvfrom;

The first allow rule allows NetLabel traffic whereas the second allow rule allows Labeled IPsec traffic. This duplication of access checks led to some interesting situations when only one peer labeling mechanism was in use, which happens to be the common case. The problem is that if you were using Labeled IPsec to browse the web you would need the following permissions:

allow firefox_t unlabeled_t:tcp_socket recvfrom;
allow firefox_t apache_t:association recvfrom;

You notice how the first access check requires access to unlabeled_t? This makes sense as NetLabel is not in use for this particular connection and is not able to generate a peer label, but it can be very confusing for users, policy writers and pretty much everyone else. The same problem happens when NetLabel is used and Labeled IPsec is not.

The solution to this was to consolidate the two access controls into a single access control. Doing so would not only simplify SELinux policy but also allow us to cleanup and consolidate much of the related SELinux kernel code. The new network peer controls work by deriving a single peer label for a network packet and then applying a single access check. In the rare case that multiple peer labels are present on a packet they must be equivalent for the packet to be considered valid; invalid packets are dropped. The resulting single access check looks like this:

allow socket_t peer_t:peer recv;

The new access check behaves exactly like the previous checks in that socket_t is the security label of the receiving socket and peer_t is the packet’s peer label (unlabeled_t when no peer labeling is in use). The difference being that it is only evaluated once per packet and is protocol agnostic; a big improvement on both counts.

While I believe most would agree that the new network peer controls are a welcome change, the fact remains that they are a change and a mechanism must exist to allow a smooth transition between the old and new controls. To solve this problem the concept of “policy capabilities” (yes, I know it is a poor name) was introduced which allows policy writers to selectively enable or disable kernel features. As of right now I am not aware of any SELinux policy, including the Reference Policy which enables the new controls. You can check your own system by running the following command (0 means the legacy controls are in use, 1 means the new controls are in use):

# cat /selinux/policy_capabilities/network_peer_controls

Later on I’ll explain how to enable the new network peer controls, but first I need to describe the new network ingress/egress access controls which are also enabled by the “network_peer_controls” policy capability. I don’t want anyone having a nasty surprise :) More on the ingress/egress controls in the next post.