Mandatory Access Control (MAC) Framework is one of the less known FreeBSD features. Let’s take a look on how to use it.

Discretionary Access Control is the most popular access control model. It’s used by the standard UNIX tools like chmod and chown or file access control lists. This model is very simple and, as the name suggests, depends on permissions granted by the owner of particular resource (file, etc…).
If you need to enforce some specific rules DAC isn’t enough. There is where Mandatory Access Control comes into play. It allows you to create a set of policies that enforce certain rules, overwriting DAC permissions when required.

The implementation of MAC model in FreeBSD is the MAC Framework. Primary work was done as part of TrustedBSD project. You can use it with well known models like MLS and Biba or create your very own policy. The framework exposes about 250 operations that can be hooked to allow very flexible and strict policy creations. There can be multiple policies connected to any of the entry points.

Let’s create very simple (and primitive) policy that limits the socket operations based on group membership. We’ll hook mpo_socket_check_create operation and decide whether to allow or deny the operation based on credentials of process calling socket(2) syscall. One group will have socket operations denied completely, the second one will be allowed only to access local sockets (AF_UNIX).

static struct mac_policy_ops nonet_ops =
{
    .mpo_socket_check_create = nonet_socket_check_create,
};

MAC_POLICY_SET(&nonet_ops, mac_nonet, "MAC/NONET",
    MPC_LOADTIME_FLAG_UNLOADOK, NULL);

Now we want to check if the calling process if a member of one of our groups and decide whether to allow the call or deny it.

if (nonet_gid >= 0) {
    for (int i = 0; i < cred->cr_ngroups; i++) {
        if (nonet_gid == cred->cr_groups[i]) {
            return (1);  // DENY
        }
    }
}

nonet_gid can be embedded in the module code or can be set via sysctl(3).
The full code can be found here.



Leave a comment