Validating IP Addresses

UPDATE: Added terminating ‘$’ in ipv4 regex as noted in comment from raorn.

I’ve been working on a fix to a system script that passes around and manipulates IP addresses. With IPv6 becoming more prevalent this script must work with IPv6 addresses not just v4. While working on this and digging around the web I ran across some stuff that I think is worth sharing.

The first thing I always do when I’m working with a new data format is writing a script / function that can be used to validate it. Here’s what I came up with for IPv4 and IPv6.

IPv4 Regex

With IPv4 this pretty boring and can be done with a one line regular expression (regex) that’s all over the web. I clean things up a bit by using shell variables but the regex should be clear:

#!/bin/sh
QUAD="25[0-5]|2[0-4][0-9]|[0-1]?[0-9]?[0-9]"
is_ipv4 () {
    echo $1 | grep --silent "^(${QUAD})(.(${QUAD})){3}$"
    if [ $? -eq 0 ]; then
        return 1
    fi
    return 0
}

is_ipv4 $1
if [ $? -eq 1 ]; then
    exit 0
else
    echo "Invalid IPv4 address." >&2
    exit 1
fi

Nothing earth shattering.

IPv6 Regex

Working with IPv6 addresses is a bit more complex. To compensate for the larger addresses size when representing IPv6 addresses in text, the RFC recommends a canonical textual representation with rules that allow for compression (called “zero folding”). Addresses represented in this compressed format are more difficult to validate with just one regex and the regex is much longer:

#!/bin/sh
WORD="[0-9A-Fa-f]{1,4}"
# flat address, no compressed words
FLAT="^${WORD}(:${WORD}){7}$"
# ::'s compressions excluding beginning and end edge cases
COMP2="^(${WORD}:){1,1}(:${WORD}){1,6}$"
COMP3="^(${WORD}:){1,2}(:${WORD}){1,5}$"
COMP4="^(${WORD}:){1,3}(:${WORD}){1,4}$"
COMP5="^(${WORD}:){1,4}(:${WORD}){1,3}$"
COMP6="^(${WORD}:){1,5}(:${WORD}){1,2}$"
COMP7="^(${WORD}:){1,6}(:${WORD}){1,1}$"
# trailing :: edge case, includes case of only :: (all 0's)
EDGE_TAIL="^((${WORD}:){1,7}|:):$"
# leading :: edge case
EDGE_LEAD="^:(:${WORD}){1,7}$"
is_ipv6 () {
    echo $1 | grep --silent "(${FLAT})|(${COMP2})|(${COMP3})|(${COMP4})|(${COMP5})|(${COMP6})|(${COMP7})|(${EDGE_TAIL})|(${EDGE_LEAD})"
    if [ $? -eq 0 ]; then
        return 1
    fi
    return 0
}

is_ipv6 $1
if [ $? -eq 1 ]; then
    exit 0
else
    echo "Invalid IPv6 address: $1" >&2
    exit 1
fi

Folks on the web have got it right too and I definitely took a queue from Vernon Mauery. I got a bit caught up in the differences between addresses from RFC4291 and the recommendations in RFC5952. The prior allows for zero folding of single 16-bit 0 fields while the latter discourages this. As the “robustness principle” dictates this validation script will identify addresses with zero folded single 16-bit 0 fields as valid but tools producing addresses should not.

I haven’t taken on any of the weirdness that are mixed hexadecimal and dot decimal notations … those will remain for the interested reader.

Understanding Multi-Level Security part 4

In my last post on this topic, we got into what I would consider the second half of Multi-Level Security (MLS). Here we discussed categories, also known as compartments and how they relate to the security model itself. We then extended the short-hand logic adapted from Chu and Older to allow for reasoning through access control decisions regarding categories.

I’ll be following the format of my previous two posts under this tag, so in this post we’ll be getting our hands dirty in the SELinux MLS policy. We’ll go through these constraints and work through them using the logic hopefully making some sense of the rules and the policy they implement.

SELinux MLS Constraints

I think I covered enough background on SELinux to just dive into the specific constraints relevant to the policy we’ve been discussing. I’ve used file read / write in previous examples so we’ll stick to that again in this post. We’ll start with simple read case, then move on to conclude the discussion begun in my last post about constraints on categories for subjects writing data to objects. We may even see the “high-water mark” again 🙂

File Read MLS Constraint

Let’s begin with the SELinux mlsconstraint for read operations on files:

# the file "read" ops (note the check is dominance of the low level)
mlsconstrain { dir file lnk_file chr_file blk_file sock_file fifo_file } { read getattr execute }
	(( l1 dom l2 ) or
	 (( t1 == mlsfilereadtoclr ) and ( h1 dom l2 )) or
	 ( t1 == mlsfileread ) or
	 ( t2 == mlstrustedobject ));

In a previous post I went blow by blow through each line of the constraint; we won’t bother with that again. Instead let’s look at the first line, which embodies the simple security property, and frame this in the logic we’ve recently expanded to reason over categories.

When we say that one label dominates another as in the dom operator from the constraint, both the sensitivity and the category component of the label must dominate. For the sensitivity we’ve established that the label set is a partial order where the le_{s} operator denotes dominance:

<br /> Unclassified le_{s} Secret le_{s} Top Secret<br />

In my last post we discussed how dominance is proven for a category set. Specifically, the category set A dominates another set of categories B if A is a superset of B. We represent this in the logic with the same operator using a different subscript: le_{c} c to denote category instead of s for sensitivity.

With this new operator we can formalize the dom operator from the mlsconstrain but, if you’ve got a good eye for detail you’ve probably noticed that our logic doesn’t have the facilities to deal with SELinux MLS ranges. The MLS parameters from the mlsconstrain statements are either the high or low portion of the MLS range. To accommodate this we add subscripts l and h to the slev and cat operators to access the low and high parts of the range respectively.

With these minor modifications we can represent the full logical expression that must be satisfied in order to conclude a read operation should be granted. Let’s call this implication the read implication for reference.

<br /> (( slev_{l}(O) le_{s} slev_{l}(S) ) land ( cat_{l}(O) le_{c} cat_{l}(S) )) supset read<br />

A Proof using the Read Implication

Any reasoning using this system must be done in the context of a specific example. Let’s construct one for a subject and object then work our way through using the read implication.


Our subjects MLS range will be:

<br /> Unclassifid - Secret:{ C_{0}, C_{1} }<br />

And our objects MLS range will be only a single level as it’s very rare for a passive object on disk to have a high and low MLS component. We can think of this as the low and high MLS levels being the same:

<br /> Secret:{ C_{1} }<br />

I’m not feeling very imaginative today as you can see by my categories simply being C_{0} and C_{1}. Regardless we’ve got enough information to do begin a proof with the aims to satisfy the left side of the read implication, which, in plain english, requires that the subjects low MLS label must dominate that of the object.

At this point the logic becomes a mechanical exercise in which we simply show that the dom relationship holds … I can see you getting bored but don’t worry, there is a twist but let’s start with our assumptions restated from above in something closer to the form we’d want in a proof:

<br /> slev_{l}(S) = Unclassified \<br /> slev_{l}(O) = Secret \<br /> cat_{l}(S) = { } \<br /> cat_{l}(O) = { C_{1} }<br />

Here’s the twist: we’ve got no way to reasonably conclude read from these assumptions. This is because we would need to derive the left side of the read implication from which we could conclude read. We can however see from our given labels that:

<br /> Secret notle_{s} Unclassified \<br /> slev_{l}(O) notle_{s} slev_{l}(S)<br />

and

<br /> {} notsupset { C_{1}} \<br /> cat_{l}(O) notle_{c} cat_{l}(S)<br />

Both the sensitivity and the category of the subject’s low MLS component DO NOT dominate that of the object. So what’s a subject to do?

Current Level vs Clearance

We need the subjects low MLS level to increase. The mechanics of how this happens and how it’s done in SELinux are out of scope here (that’s how I get out of describing something complicated) but let’s assume there’s a safe way for a subject to do this and that they can’t simply do so at will.

The low MLS level is often referred to as the users “current” level, and the high MLS level as the users “clearance”. The term “high-water mark” comes from the analogy made between the users current level rising as a result of subject actions and the mark made by rising water on a surface.

But I’m digressing. Now that our user can safely increase their current/low MLS level we can get back to our example.

Finally Reading the File

Let’s assume that our user can increase their low/current MLS label to the minimum possible for their access to be granted, assuming of course it will never exceed their clearance. We now use these values as the assumptions in our proof:

<br /> slev_{l}(S) = Secret \<br /> slev_{l}(O) = Secret \<br /> cat_{l}(S) = { C_{0}, C_{1} } \<br /> cat_{l}(O) = { C_{1}}<br />

We then use these to show that the read should be allowed:

<br /> Secret le_{s} Secret \<br /> slev_{l}(O) le_{s} slev_{l}(S) \<br /> { C_{0}, C_{1} } supseteq { C_{1} } \<br /> cat_{l}(O) le_{c} cat_{l}(S) \<br /> ( slev_{l}(O) le_{s} slev_{l}(S) ) land ( cat_{l}(O) le_{c} cat_{l}(S) ) \<br /> (( slev_{l}(O) le_{s} slev_{l}(S) ) land ( cat_{l}(O) le_{c} cat_{l}(S) )) supset read \<br /> read<br />

This is only the first of several logical constraints from the mlsconstraint for file read operations. In a previous post I’ve tried to fit SELinux types and comparisons there of into this logical framework but it’s not a very natural fit. Since this post is already getting close to 1200 words and we still haven’t talked about the constraint on write operations I’ll put off the discussion of types for now.

File Write MLS Constraint

Now that we’ve examined the read operation, it’s time to take on writing to files. The SELinux constraint for this case is as follows:

mlsconstrain { file lnk_file fifo_file dir chr_file blk_file sock_file } { write create setattr relabelfrom append unlink link rename mounton }
	(( l1 eq l2 ) or
	 (( t1 == mlsfilewritetoclr ) and ( h1 dom l2 ) and ( l1 domby l2 )) or
	 (( t2 == mlsfilewriteinrange ) and ( l1 dom l2 ) and ( h1 domby h2 )) or
	 ( t1 == mlsfilewrite ) or
	 ( t2 == mlstrustedobject ));

Like before we’re going to ignore all except the first part of the constraint: l1 eq l2. This is the rule that implements the expected “high-water mark” behavior. The remaining parts of the constraint are relaxations of this rule and should be used with caution.

You’ll notice that the constraint for writing differs from reading. It doesn’t require l1 dom l2, instead the current MLS level for the subject must be equal to that of the object: l1 eq l2. But instead of introducing new logic to express equality, we’ll use the reflexive property of the le_{s} and le_{c} operators:

<br /> ( slev_{l}(A) le_{s} slev_{l}(B) ) land ( slev_{l}(B) le_{s} slev_{l}(A) ) supset A =_{s} B<br />

Notice this deals only with sensitivity so we’d have to show the same for the category component of the MLS label. Accounting for both sensitivity and categorys we would say that if A = B then the reference monitor can conclude write (allow the write operation). We’ll call this implication the write implication for future reference:

<br /> ( ( ( slev_{l}(A) le_{s} slev_{l}(B) ) land ( slev_{l}(B) le_{s} slev_{l}(A) ) ) land ( ( cat_{l}(A) le_{c} cat_{l}(B) ) land ( cat_{l}(B) le_{c} cat_{l}(A) ) ) ) supset write<br />

Let’s look at a write request in the context of the MLS ranges for our subject S and object O from the read example above. Again, we’re concerned with the low part of the MLS label so the assumptions are:

<br /> slev_{l}(S) = Unclassified \<br /> slev_{l}(O) = Secret \<br /> cat_{l}(S) = { C_{0}, C_{1} } \<br /> cat_{l}(O) = { C_{1} }<br />

From the read example we know that initially the current MLS level for the subject doesn’t dominate that of the object:

<br /> slev_{l}(O) notle_{s} slev_{l}(S)<br />

so again we’re in the situation where the subect must increase it’s low MLS label to access the object O. Let’s assume this is possible but it’s not enough. We have to account for the category component as well and as we saw above, the category set of the subject and object must be the same. Our subject must then drop C_{0} from its category set as well:

<br /> slev_{l}(S) = Secret \<br /> cat_{l}(S) = { C_{1} }<br />

Now that we’ve increased the low (current) MLS label and dropped the required category from the subject S we can use the logic to prove that our reference monitor will allow the write operation using the write implication from above.

<br /> Secret le_{s} Secret \<br /> { C_{1} } supseteq { C_{1} } \<br /> slev_{l}(S) = Secret \<br /> slev_{l}(O) = Secret \<br /> cat_{l}(S) = { C_{1} } \<br /> cat_{l}(O) = { C_{1} } \<br /> slev_{l}(S) leq_{s} slev_{l}(O) \<br /> slev_{l}(O) leq_{s} slev_{l}(S) \<br /> ( slev_{l}(S) leq_{s} slev_{l}(O) ) land ( slev_{l}(O) leq_{s} slev_{l}(S) ) \<br /> cat_{l}(S) leq_{c} cat_{l}(O) \<br /> cat_{l}(O) leq_{c} cat_{l}(S) \<br /> ( cat_{l}(S) leq_{c} cat_{l}(O) ) land ( cat_{l}(O) leq_{c} cat_{l}(S) ) \<br /> ( slev_{l}(S) leq_{s} slev_{l}(O) ) land ( slev_{l}(O) leq_{s} slev_{l}(S) ) land ( cat_{l}(S) leq_{c} cat_{l}(O) ) land ( cat_{l}(O) leq_{c} cat_{l}(S) ) \<br /> ( slev_{l}(S) leq_{s} slev_{l}(O) ) land ( slev_{l}(O) leq_{s} slev_{l}(S) ) land ( cat_{l}(S) leq_{c} cat_{l}(O) ) land ( cat_{l}(O) leq_{c} cat_{l}(S) ) supset write \<br /> write<br />

An operator to express equality among sensitivities and categories would only serve to shorted the conjunctions in the last two expressions so it may be useful, though only as syntatic sugar.

The High-Water Mark

Something that we glossed over are the implications of subject actions possibly resulting in a subjects current MLS level increasing. This is a reasonable requirement given that we don’t want to allow a subject to do something like reading a file in one category and then writing data from it to a file with a different category label. We must assume that when the system increases the current MLS level for a subject the operation is safe.

By “safe” we mean there would need to be mechanisms to prevent the user from:

  1. setting their low MLS level to anything above their high level
  2. that the high level is static and determined by the security administrator
  3. that the user cannot decrease their low MLS level without having their environment thoroughly scrubbed to prevent information leakage

These details are only peripheral to the main focus of this post but the details are often what make or break a system. Doing things like sanitizing a users environment to prevent data leakage can be extremely invasive and can disrupt the users work flow significantly.

Conclusion

There are two things to take away from this post one positive, one negative. First, the logic we’ve been using up to this point is able to represent the reasoning a reference monitor would do when evaluating either a read or a write an access request. This could be used to take a mechanical proof system and use it to implement the core of the SELinux reference monitor. That’s pretty cool.

The negative that’s come to light is that the mechanism by which the “current” MLS label must be managed has a notably temporal aspect. That is to say that when a subject reads or writes data to an object its low MLS label must change to prevent data leakage. All subsequent accesses will then be made in the context of this new label. This is temporal because the sensitivity and categories returned by the functions slev and cat will change based upon the actions of the subject over time.

The management of subject and object labels (basically how they change over time) can, and probably should be a distinct and separate operation from the mechanism making access control decisions. In this way the reference monitor can still us this logic to prove which accesses should be allowed and another separate module using a completely different reasoning engine can be responsible for label management.

Next Time

This is the last of my posts on MLS in SELinux and representing access control decisions with logic. My next post will be about the work I’ve done implementing the sVirt requirements in a simulated environment to demonstrate the separation properties that can be obtained using MLS categories.

Ethernet Bonding on Debian Squeeze

Spent a few minutes searching for a howto for setting up ethernet interface bonding on a new file server I’m building today. Nothing special but I found a bunch that aren’t that great … I know, welcome to the internet right? But I did find one that’s awesome from tuxhelp.org.

My final config went like this:

echo -e "bondingnmii" | sudo tee -a /etc/modules

With an /etc/network/interfaces file that looks like this:

auto lo bond0
iface lo inet loopback

iface bond0 inet dhcp
    bond_mode balance-rr
    bond_miimon 100
    bond_downdelay 200
    bond_updelay 200
    slaves eth0 eth1

What was lacking in all other (even Debian specific) howto’s is that they always use direct invocation of ifenslave and pass options to the bonding driver manually. IMHO it’s so much nicer to use the facilities built in to ifup like the slaves option instead of using something like:

up /sbin/ifenslave bond0 eth0 eth1

That said I haven’t had much luck finding documentation for options like this specific to a driver and how to use them in the interfaces file. Given the above example I can guess but I’m looking for a definitive source … Anyone out there know?

Exim + Sieve issues

I spent much longer than I’d like to admit moving my mail server today. The Debian exim4 package is very easy to configure and setting up TLS and authentication is a snap with the help of a very good Debian Administration article. Also I’ve had to tweak the address_file transport to support Sieve and the fileinto action.

What I’m writing now is mostly for my own benefit so I don’t have to look this same crap up in a few years when I move my mail server again:

TLS and Auth

The howto above uses the standard exim auth from /etc/exim4/passwd but if you want to use something like the courier-authdaemon all you need to do is go further down in /etc/exim4/conf.d/auth/30_exim4-config_examples to uncomment a later section:

plain_courier_authdaemon:
login_courier_authdaemon:

All authentication should be done over TLS and enabling this only requires a private key, a certificate and adding this file: /etc/exim4/main/000_localmacros:

MAIN_TLS_ENABLE = yes
MAIN_TLS_CERTIFICATE = /etc/ssl/certs/example.org.cert
MAIN_TLS_PRIVATEKEY  = /etc/exim4/example.org.key

The Debian exim daemon is running as the user Debian-exim so it won’t be able to access files in /etc/ssl/private. You can either keep your secret key in /etc/exim4 as I’ve done above or add the Debian-exim user to the daemon group and make the group for /etc/ssl/private daemon. Either is reasonable but you’ll have to add Debian-exim to the daemon group anyway so it can use the authdaemon socket.

At this point you should check to be sure your mail isn’t an open relay and there are a number of tools available to test this. Some are websites that you can simply enter the IP/domain name of your mail server. Others are tools like swaks where you can test this for yourself. A good example of using swaks for testing exim4 can be found here.

Exim4 and Sieve

Finally it seems like the Exim4 package on Debian Squeeze may have a bug when it comes to delivering mail to users with Sieve filters in their .forward file. I kept getting an error stating:

R=userforward T=address_file defer (-21): appendfile: file or directory name "inbox" is not absolute

To debug this a cheetsheet for getting Exim to do your bidding essential. The best one I could find is here.

There are a number of mailing list posts out there discussing similar errors but none seemed to fix my problem. Basically the error message means that the appendfile transport isn’t able to figure out what the “inbox” from a sieve filter should be when converted to a file/directory name. I’m using maildir in a users home directory so I spent a few hours poking around that part of the configuration to no avail.

Eventually, in my old configuration, I found a patch to the address_file transport to help it figure out what “inbox” is:

--- a/conf.d/transport/30_exim4-config_address_file	2005-02-19 05:25:59.000000000 -0500
+++ b/conf.d/transport/30_exim4-config_address_file	2011-07-24 21:52:33.270494409 -0400
@@ -8,4 +8,8 @@
   delivery_date_add
   envelope_to_add
   return_path_add
+  directory = ${if eq{$address_file}{inbox} 
+                            {$home/Maildir/new} 
+                            {$home/Maildir/.${sg{$address_file}{^inbox[.]}{}}/new}
+                        }

Here comes the disclaimer: I’m no Exim hacker and I can barely figure out what this does. I found it two years ago when I was setting up my mail server and had to get Sieve filters working. When I moved this mail server today I upgraded from Lenny to Squeeze and figured this may have been fixed. It wasn’t though so I had to dig through my old configs to find it again.

Understanding Multi-Level Security part #3

There are two parts to a Multi-Level Secuirty (MLS) policy. Now that we’ve covered the sensitivity component it’s time to address the second component which is typically referred to as a category or compartment. Before we get into the rules that govern this policy component however, let’s talk about why we need them.

Sensitivities are a direct reflection of military policy for classifying sensitive data. What’s not typically known is that within each sensitivity level (Unclassified, Secret, Top Secret) there are many sub-compartments for protecting different types of data. An example in the military context would be data about political or diplomatic subjects and data about signal intelligence collection activities.

People working on sensitive data in these different areas of interest likely don’t have any need to access the other category even though both sets of data are at the same sensitivity level. The need for this additional level of separation resulted in the creation of this second component of the policy model.

Categories / Compartments

The oldest publication that I could find describing a reference monitor expected to enforce MLS categories is from a document describing the ADEPT-50 time sharing system [1]. This system was implemented as one of the first remotely accessible (terminal) main frames enforcing a military security policy. Unfortunately the ACM wants $15 for the paper which is pretty steep for the casual reader. If your company / university / local library has an ACM subscription then you should definitely pull down this article. It’s a great read.

If you can’t get the article, the relevant parts have been covered in other places. I’d specifically recommend the SELinux Wiki [2] which is accurate and relevant if a bit sparse. Further, Chin and Older [3] also give the topic consideration and apply the similar rules.

From these sources we know that a subject must poses a superset of the categories assigned to the object it wishes to access for a read operation to be granted. We represent the subset relationship where set A is a subset of set B like so:

<br /> A subseteq B<br />

Similarly we can flip the relationship around and say that B is a superset of A like so:

<br /> B supseteq A<br />

Using the language developed previously, we would say that B dominates A if B supseteq A.
Note: these are not proper subsets, the relationship holds if A and B are the same set.

The Logic

Now that we’ve established the rules governing the category component of an MLS policy let’s incorporate these rules into the logic. I’ve been using operators and logic from [3] specifically slev (), short for “security level”. In previous examples this operator has returned the sensitivity level of the parameter (a principle). Now that we’re considering categories we’ll need an operator to return the category component of the principles security label.

For this we define the function cat () which, much like slev () takes a principle as its only parameter. What’s unique though is that it returns a set containing the categories assigned to the principle. For a principle with categories { C_{0}, C_{1}} it would return:

<br /> cat (P) = { C_{0}, C_{1} }<br />

Note: the categories assigned to P may be the empty set: {} if the principle has been assigned no categories.

Now that we have a way to obtain the necessary information about principles we need an operator to compare them. Again we use the operator le_{s} to compare sensitivity labels but we now add a similar operator le_{c} to allow us to perform a comparison of category sets. To do this let’s define two principles P_{1} and P_{2}. We define their security labels like so:

<br /> cat (P_{0}) = { C_{0}, C_{1} } \<br /> cat (P_{1}) = { C_{0} }<br />

With this information we can conclude that the security label for P_{0} dominates that of P_{1} from our discussion of sets above. Specifically

<br /> ({C_{0}, C_{1}} supseteq {C_{0}}) supset ({C_{0}, C_{1}} le_{c} {C_{0}})<br />

We generalize this implication as:

<br /> (cat(P_{0}) supseteq cat(P_{1})) supset (cat(P_{0}) le_{c} cat (P_{1}))<br />

Recalling our discussion of sensitivity levels and the slev operator we would say that principle P_{0} dominates principle P_{1} if:

<br /> slev (P_{1}) le_{s} slev (P_{2}) land cat (P_{1}) le_{c} cat (P_{2})<br />

For now we’ll assume the sensitivity levels of our principles are all the same so we can focus on the categories alone.

Example: Reading a file

Now that we have all the necessary logic we can construct a more complete example. Considering a read operation, our goal is (as the reference monitor) to prove that the security label of our subject S dominates that of our object O so that we can conclude access should be granted. We represent this as the logical expression:

<br /> (cat (S) le_{c} cat (O)) supset read<br />

Now we need to make some assumptions about the system, specifically defining the categories assigned to the principles S and O (subject and object respectively):

<br /> cat (S) = {C_{0}, C_{1}}) \<br /> cat (O) = {C_{0}} \<br />

From these assumptions we can show that, using the above logic, the categories assigned to S dominate those of O:

<br /> { C_{0}, C_{1} } supseteq { C_{0} } \<br /> (cat(S) supseteq cat(O)) \<br /> (cat(S) supseteq cat(O)) supset (cat(S) le_{c} cat (O) \<br /> cat (S) le_{s} cat (O)<br />

With this we can conclude (by modus ponens) that this read operation will be allowed

<br /> cat (S) le_{s} cat (O) \<br /> (cat (S) le_{c} cat (O)) supset read \<br /> read<br />

Writing to a file

The rules governing file reads is straight forward: if a subject with the two categories C_{0} and C_{1} it should be allowed to read from an object with either category. But, as always, writing to an object is a bit different. This same subject attempting a write to the an object O with category C_{0} raises a very interesting question: the subject may contain data from either category 0 or 1 through a previous read operation, but which category will the data it writes to our object belong to? As stated, our object O has category C_{0} and we don’t have any assurance that the subject won’t write data from category C_{1}.

In [1] the authors address this concern for file creation using a “high-water mark” created from a history of all files accessed by a job / process. They do not however differentiate between read and write access on a file. Presumably the high-water mark method would also work for file writes but it would require the reference monitor to track the data categories accessed by a process and ensure that all files written be labeled with the full set of categories previously read by the process.

This loosely stated rule would be quite complex when implemented. It would require a full history of the categories accessed by a process be maintained in the kernel and that this be done for each process. For some processes this list would get quite long and take up significant storage.

An alternative approach is simply to trust the subject to write only data of the appropriate category to the file. This isn’t very viable as it has a significant short fall discussed above. Generally any approach that is described with the phrase “trust the subject” will be rejected by security auditors.

Finally the most restrictive approach would be to require a subject writing to an object to have the same set of categories as the object. This seems like the best approach as far as security is concerned but it is very restrictive and could cause over classification of newly created files if the user is not diligent.

Conclusion

The concept of categories / compartments from MLS policy fits naturally into the logic we’ve been using to reason over sensitivities in my previous posts. It’s a simple concept based on set comparisons that a reference monitor can do quickly and efficiently. This policy’s application to reading files is simple but the best way to handle writes comes with significant complications which we discussed here briefly.

Instead of trying to solve this last complication in this post I’m punting. In my next post however we’ll get into the SELinux policy. We’ll take a look at how they deal with categories in the reference policy focusing on the read operation to make concrete the logic from this post. Then we’ll move on to how refpol deals with categories in file write operations hopefully shedding some light on how a real system manages the complexity discussed in the last section of this post.

References

1 C. Weissman. 1969. Security controls in the ADEPT-50 time-sharing system. In Proceedings of the November 18-20, 1969, fall joint computer conference (AFIPS ’69 (Fall)). ACM, New York, NY, USA, 119-133. DOI=10.1145/1478559.1478574 http://doi.acm.org/10.1145/1478559.1478574
2 SELinux Wiki. Managing Security Levels via Dominance Rules. http://selinuxproject.org/page/NB_MLS#Managing_Security_Levels_via_Dominance_Rules
3Chin, Shiu-Kai and Older, Susan. Access Control, Security and Trust CRC Press, 2011. http://www.amazon.com/Access-Control-Security-Trust-Cryptography/dp/1584888628