Kernel_Killer
July 5th, 2007, 01:16
Mandatory Access Control (MAC) - BSD Extended Tutorial


Introduction

While I was planning to do a simple tutorial covering all types of MAC security, I decided that one tutorial for all would really depreciate the tutorial as a whole. Instead, I'll be doing a single tutorial on each feature, and later one to integrate them all together to better suit your needs.

The BSD Extended module helps us protect every aspect of the system's file system, and then some. We are able to set explicit permissions on what a user can do, see, and control. Not only that, we can set what uid/gid in a specific jail can do. Now that's secure! Of course, let us not get ahead of ourselves. First, we must get the feel of this, and a moderate understanding of how it works before we can really start using it.

The Basics

First off, we need to make sure that we have a kernel with the MAC option compiled in. Simply add "options MAC", and recompile your kernel. Once you have booted off the new kernel, load the BSD Extended module `kldload mac_bsdextended` (the reason we do not compile the module into the kernel itself, is that we can actually lock ourselves out of the system regardless of pure root access).

The tool we will be familiarizing ourselves with will be ugidfw. If you have used ipfw for any decent amount of time, then you will feel very comfortable with this tool. Instead of altering network traffic, we will be altering user/group traffic (If you haven't used ipfw before, don't fret, we will be covering the tool enough for any user to understand).

UGIDFW

The first thing we're going to do, is use the 'list' argument.

# ugidfw list
0 slots, 0 rules

As we can see, we don't have anything in the rule set, but as we progress later, we will see how this starts to grow, and how we can view what is being enforced. We have to keep in mind there are two ways to adding a rule to the rule set, 'ugidfw add', and 'ugidfw set rulenum'. The add command simply adds the rule to the rule set, and gives it the next highest number available in the rule set. The set command tells the firewall what number rule the new rule is. Keep in mind this is very important to know the difference between the two. The firewall uses a 'fall-through' method, so the first rule is looked at first, and then the next, and so on. If you just keep using add, then you might end up re-writing your entire rule set just to make a new rule work like it should. Of course, even with that in mind, you can never plan to make a perfect rule set without some mistakes. Removing a rule is very simple. We use the 'ugidfw remove rulenum' to remove a rule via rule number.

Now, let's take a look at adding rules, and applying them.

# ugidfw set 10 subject uid 1000:2000 ! gid 1000 jailid 2 object filesys /usr/ uid_of_subject type rd mode rwx

Before you become overwhelmed, we will break it down piece by piece, and realize that it really isn't as bad as it looks. As you can see we first set the rule number to "10". The next part identifies the settings for the subject, or in other words the user/group we are applying the rule to. First, we tell it the user id. In this case we gave it a scope of user ids, starting with the lowest to the highest. Next we specified that the rule only apply to users NOT in the group 1000. So our rule applies to user 1000 through 2000, as long as they aren't in group 1000 (the uid and gid can also be set by name). The last part of our subject criteria is the jailid. This tells the system what jail to apply this rule to.

The 2nd half of our rule deals with the object, or what we are allowing or disallowing the subject to access. In this case, we are working with the mount point "/etc" (this does not work for separate directories, just mount points). After we tell it what we want control (if anything in specific), we then specify how we want to control it. In this case, we first use the uid_of_subject argument. This tells the system that the uid of the subject (1000-2000) must match the owner of what we are controlling. In other words, if the owner happens to be user 1000-2000, then they match the rule. Then the type of access is granted or taken away from who matches the rule. There are 8 types of access you can give with this argument (per UGIDFW(8)):

a any file type
r a regular file
d a directory
b a block special device
c a character special device
l a symbolic link
s a unix domain socket
p a named pipe (FIFO)

Looking at our example, you can see that we only gave access to regular files, and directories. Last, we tell the rule what permission access we want to give the subject. These are not much different from the access flags from chmod, but have a few more:

a administrative operations
r read access
s access to file attributes
w write access
x execute access
n none

As we can see, we gave the subject the basic abilities, read, write, and execute.

Using it in Real-World Situations

Now that we have a basic understanding of using ugidfw, now we need to start adding rules, and see how they effect the system.

What I've done is made a new user named MacUser with the uid 1002, and the gid 1002 respectively.

Through this part of the tutorial I will mark root actions with a "#" shell prompt, and ">" with the subject's actions.

We can easily limit any user to what they can or cannot do on each mount point. This is where the good practice of using multiple partitions really comes into play. On fileservers, we can retstrict users from accessing anything but the files in their network share.

> ls /
COPYRIGHT compat lib rescue usr
.....

# ugidfw set 20 subject uid 1002 object ! filesys /home type rd mode n
# ugidfw set 30 subject uid 1002 object filesys /usr type rd mode rxs

> ls /
ls: dev: Permission denied

Here's what we did. We gave the user no access to all the directories except for /home. Then we gave the user access to /usr so that they could actually run the command 'ls'. Now, if we wanted to make sure that only a user could see their own documents, and couldn't go into any other user's folder, we could do something along the lines of this:

> ls -l /home
total 4
drwxr-xr-x 3 MacUser MacUser 512 Jul 4 17:20 MacUser
drwxr-xr-x 29 Syn Syn 1536 Jul 4 16:25 Syn

# ugidfw set 10 subject uid Syn:MacUser object uid Syn:MacUser filesystem /home ! uid_of_subject mode n

user Syn

> ls -l /home
ls: MacUser: Permission denied
total 2
drwxr-xr-x 29 Syn Syn 1536 Jul 4 16:25 Syn

As you can see, we restricted the anyone but the owner access to their own folder. Instead of gid numbers, we used a scope of users. The names are still being used in numerical order just as if we used numbers. What we actually did is said that if there is a file or directory in /home where the owner was Syn - MacUser, to allow them access to their own folder, but to disallow anyone else in the scope access.

For any of you that are familiar with UNIX Domain Sockets or FIFO Pipes, keep in mind you can also restrict these as well in the type field (I will refrain from explaining this only for the reason that the the tutorial would require experience in the C language).

So what about the daemon users? Well, if you take a look at '/etc/rc.bsdextended', you'll realize by default, each one is set to have rwxs on the entire system. You will also notice that all the users >= 1001 are untouched, and everyone else not mentioned is given an explicit rule to have access to nothing at all.

Last, remember that the BSD Extended module is actually an overlay for the base permission system. If a folder doesn't allow someone to write to a folder, you cannot explicitly force write access through the BSD Extended module.

Now that you have a better understanding of how to use this module, now you can plan you security better, and hopefully utilize the the power of the features offered by this module. While we only skimmed the surface, we will cover a more in-depth answer later.

NOTE: While this tutorial does not cover the complete use of the module, a later tutorial will in conjunction with the other modules. Still, to keep all aspects complete, these are the variables to be added to have BSD Extended startup with the system:

/etc/rc.conf
ugidfw_enable=YES

loader.conf
mac_bsdextended_load=YES

Copyright Network Synapse (http://www.networksynapse.net)