Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

From: Kyle Moffett
Date: Sat Aug 11 2007 - 17:47:40 EST


On Aug 11, 2007, at 17:01:09, Casey Schaufler wrote:
[SELinux...] which can do *all* of this, completely and without exceptions,

That's quite a strong assertion.

It is, but I stand by it. If anyone can point out some portion of this which *cannot* be implemented as SELinux policy I will humbly step completely out of this discussion.


but one does have to have complexity in order to handle everything from CD burning in X, to Apache daemons, to only allowing Top- Secret-level logins over the IPsec tunnel on the Top Secret network, etc.

I do not agree with you. The MLS systems from the 1990's could do all that (except the IPsec tunnel, the function of which was preceeded by TSIG interfaces and protocols) without the complexity required by SELinux policy.

No, most/all of the MLS systems from the 1990s did not integrate Bell- LaPadula, Type Enforcement, and Role-Based-Access-Control together. In addition, none of the MLS systems from the 1990s had modifiable constraints the way SELinux does (for example I can modify the fundamental policy to allow a process whose domain has the "i_am_special" attribute to override the BLP model for certain target types in certain special situations, all depending on how I apply that attribute and those types).

For example, this set of rules basically defines your described "read-vs-write-vs-exec" policy as best I can figure out:

user uu roles rr;

role rr types { star floor hat huh };

define(`r',`
allow $1 $2:file { read getattr };
allow $1 $2:socket { read getattr getopt recvfrom recv_msg };
allow $1 $2:ipc { getattr read associate unix_read };
## List of more "read" allow rules for different types of objects... ##
')

It would be instructive for those who are not well versed in the nuances of SELinux policy if you actually spelled out the whole thing, rather than using "## and more ##". Part of the point of Smack is the makeup of the full list that would be required here.

Well, yes, but how exactly do you define "read", "write", and "execute" for the full list of SELinux object classes found here:
http://www.tresys.com/selinux/obj_perms_help.html

I was considering compiling the complete list, but such an exercise would take me at least an hour to do properly and which categories individual permissions should be placed in could be argued for weeks. The SELinux reference policy doesn't even completely qualify all of those things with MLS restrictions, and they're the ones who got the hooks implemented originally.

Specifically, do you classify the bind() syscall as a "read" or a "write" operation. How would you classify accept()? In order to support the full range of security controls that Linux has hooks for, you would need to identify a list of how to define "read", "write", and "execute" for every object-class in that massive list. Furthermore you would need to ensure that the definitions work for every process. From the variations between different portions of the SELinux ref policy, I contend that there is no single definition of "read", or "write" which works for all usages of a given object-class.

And now to describe these rules:

Smack defines and uses these labels:
"*" - pronounced "star"
"_" - pronounced "floor"
"^" - pronounced "hat"
"?" - pronounced "huh"

The access rules enforced by Smack are, in order:

1. Any access requested by a task labeled "*" is denied.
2. A read or execute access requested by a task labeled "^"
is permitted.
3. A read or execute access requested on an object labeled "_"
is permitted.
4. Any access requested on an object labeled "*" is permitted.
5. Any access requested by a task on an object with the same
label is permitted.
6. Any access requested that is explicitly defined in the loaded
rule set is permitted.
7. Any other access is denied.

## These are calls to the above macros which plug in the necessary
arguments
r(hat, {*})
x(hat, {*})
r(~{star}, floor)
x(~{star}, floor)
r(~{star}, star)
w(~{star}, star)
x(~{star}, star)
r(~{star}, self)
w(~{star}, self)
x(~{star}, self)
## Include your "loaded rule set" here ##

What would that look like?

The same kind of thing as the r, x, and w above, with occasional "type my_type_t; role rr types my_type_t;" to declare a type before use. If you wanted to add support for attributes which apply to multiple types, you could put those after a comma after the "type my_type_t" portion of the type declaration.

Smack rule sets can be easily defined that describe Bell&LaPadula sensitivity, Biba integrity, and a variety of interesting configurations. Smack rule sets can be modified on the fly to accomodate changes in the operating environment or even the time of day.

SELinux can do this as well. It even includes support for conditional policy:

bool foo_can_do_logging true;
if (foo_can_do_logging) {
allow foo_t foo_log_t:file { create read getattr append };
}

You have to build the booleans into the policy in advance.

Well, yes, but a policy which completely ignores


The SELinux tools also have support for policy modules, so you can extend the policy without modifying the base system.

And that's a good thing, but you still have to compile and reload your policy, and maybe relabel you filesystem when you do so.

What exactly prevents you from having to "reload" and "relabel the filesystem" when using your system? When you change the meaning of new types, you clearly would need to first reload the policy then relaunch programs or relabel files to take advantage of the new types, otherwise they would be categorically ignored by the system.

The only difference is the "compile" step, which converts from a reasonably modifiable language into a binary format which the kernel can efficiently parse. I suspect you'd get shot down instantly if you tried to stuff the policy compiler in the kernel too.

Some practical use cases:

Hierarchical levels. The less common of the two usual uses for MLS systems is to define hierarchical levels, often unclassified, confidential, secret, and so on. To set up smack to support this, these rules could be defined:

C Unclass rx
S C rx
S Unclass rx
TS S rx
TS C rx
TS Unclass rx

A TS process can read S, C, and Unclass data, but cannot write it. An S process can read C and Unclass. Note that specifying that TS can read S and S can read C does not imply TS can read C, it has to be explicitly stated.

The big problem here is the duplication. Say you have a locked- down Apache configuration and you want to run 2 apache processes, one at Secret and one at Top-Secret. Under your model you have to copy-paste the policy and make sure to apply fixes/changes to both places.

I'm sorry, but I don't understand your point at all.

Ok, let's say you want to use your model to restrict Apache and MySQL running at the same classification level. You have to define combined labels, such as:
S_Apache S_MySQL rx
[...]

Now if you want to run an apache/mysql pair at TS, you have to copy all those rules and substitute "S_" with "TS_". With SELinux, you don't have to do anything other than define new labels for the secret- web-docs and the top-secret-web-docs folders, because the Apache *type-enforcement* policy is completely independent from the MLS levels. You can write a 5-line policy to label 2 slightly different initscripts with different range-transitions. The only difference between them will be the paths inside and the labels on them. The rest of apache's policy stays the same.

This is exactly what my company does, but the ability to restrict *exactly* what mechanisms are used is important. You restrict against file-system access implicitly, whereas SELinux does it explicitly:
allow foo_t foo_sock_t:socket { .... };
versus:
allow foo_t foo_log_t:file { ... };

With SELinux you can also allow your program to create files labelled as "log files" in one directory in one type and "transfer files" in another directory in a completely different type.

I would be very interested to see the policy that your guard box uses.

Unfortunately that's still considered company-proprietary information at the moment, although that's likely to change at some point in the future. We're not all that different from the httpd policies and similar stuff found in the SELinux "refpolicy" available from tresys' website.

I'm proud to say that the software of the company I work for does not need any extra privilege at all (aside from binding to ports <1024) to run under SELinux with strict MLS.

That is an aspect of SELinux that has its dark side. While you are not explicitly violating a policy (e.g. requiring a capability) you are doing things that some of us would argue ought to require a capability. SELinux integrates privilege into the policy mechanism.

How does "moving data around a system" require root privileges? Why should it? Admittedly, moving data from "TopSecret" to "Secret" requires a process labeled with a range including at least "S-TS" *and* with the MLS "read-to-clearance" attribute, but you can be as stingy with that attribute as you like. That attribute is explicitly given its magic abilities in the mlsconstrain rules defined in the policy file itself, so it's also possible to do information flow analysis on it.

Smack does not. Smack is designed to use traditional Linux mechanisms for privilege.

Under Trusted Solaris and such we needed all sorts of dirty privilege hacks to relabel the files consistently, but under SELinux the policy does all the relabeling for us, we don't need to do a thing.

Yeah. Well, you won't like Smack then. Implicit relabeling of processes and files does not happen. This is a major philisophical difference.

Here's an argument for why implicit labeling is good and explicit labeling is bad: If your process wants to label things explicitly it needs special privileges to do so, and it can abuse those privileges. Under SELinux the labeling is implicit *AND* the transitions are entirely within the policy. This means that you can do information flow analysis (as I described above) and it *ALSO* means that the program cannot possibly "abuse" its privileges because it has none. When httpd creates a file in its log directory that file is created with httpd_log_t and it is limited to being able to append to the file *only*. So when your apache gets compromised, the attacker has NO WAY to modify the logs, and you can prove this with a 60-second study of the policy. In fact, on my companies productions systems I can *PROVE* that the only way to get audit data out of the system is via a local TTY by interacting with system_u:object_r:local_login_t:SystemLow-SystemHigh to log in with a process of someluser:auditreview_r:auditreview_t:SystemHigh, which may read the audit files, or otherluser:auditadmin_r:auditadmin_t:SystemHigh, which may read and delete the audit files. I can even prove that no process on the system is allowed to modify the files after they are created, barring a kernel-level vulnerability.

So the implicit labeling of files actually restricts every process *FURTHER*. They no longer have even an inkling of a choice about file labeling, and that's a GOOD thing. The only remaining extension we need to really thoroughly lock down our systems is to allow last- path-component matching in type-transitions and allow rules (EG: When "vipw" creates a file named "passwd" in an "etc_t" directory, it is automatically labeled "etc_passwd_t") From what I understand Stephen Smalley and others are thinking that over even now. I'll do it myself as soon as I get time at work beyond prepping systems for shipping to clients if they haven't finished it by them.

Cheers,
Kyle Moffett

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/