Working with TrustedBSD in Mac OS X

For a long time, we have been controlling access to files and applications at our computers using Discretionary Access Control (DAC). Usually, this approach looks like a combination of a user with restricted privileges having access to a number of strictly defined resources (files, applications, etc.) and an administrator with full access to all system resources.

Generally, this approach seems to be mostly sufficient, and — for users — sometimes even excessive, which is confirmed by a great number of users working with administrator privileges at their computers.

However, a lot of situations occur where DAC is insufficient not only for corporate but also for home users. A typical situation for corporate users is deployment of DLP (Data Loss Prevention) solution where users, even with administrator privileges, should not be permitted to manage it. Another example of insufficiency of DAC for both corporate and home users is anti-virus systems and firewalls, e.g. a well known Trojan — Flashback.C OS X — that simply removed the built-in Apple anti-virus from startup which was mainly possible due to default administrator priviliges of a MAC OS X user (i.e. sudoer by default).

Actually, the idea of this article emerged from a communication with a DLP solution developer. This company wanted to protect their product from removal by users with administrator privileges on Mac OS X computers.

Happily, this task has an easy solution in MAC OS X as it has built-in tools for deployment of a MAC (Mandatory Access Control) approach. Generally, MAC approach assumes assigning security attributes to objects with similarly attributed information and granting access to these objects for requests from users with sufficient permissions. So in case of MAC, no matter whether a regular user or an administrator attempts to access the resource, there is a fine option to restrict such attempt and terminate it if necessary.

There is a variety of MAC implementations for different platforms, e.g. SELinux and AppArmor for Linux, Solaris Trusted Extensions for Solaris, and TrustedBSD for BSD or Mac OS X. In this article, we are talking only about TrustedBSD for Mac OS X.

TrustedBSD framework for Mac OS X was created long ago, and support for most interfaces was introduced since Mac OS X 10.5. The framework itself is actively employed by Apple both in Mac OS X and iOS to ‘sandbox’ (i.e. to isolate) applications. Unfortunately, the existing sandbox does not protect an application from a user as it is intended to protect users from applications, and is not suitable for addressing the above problem. However, it doesn’t prevent us from using TrustedBSD if we write an extension for it.

Apple documentation for TrustedBSD is rather poor, while FreeBSD Architecture Handbook is much more informative on TrustedBSD and is more helpful in searching the information. Generally, the TrustedBSD interfaces for Mac OS X and FreeBSD are similar, but their names are slightly different, e.g. mpo_vnode_check_open callback function (which will be used in the example below) in case of FreeBSD is named mpo_check_vnode_open. This hinders your work with the framework to some extent as you have to guess the interface names, but it enables you to get a more detailed picture of the situation.

With TrustedBSD, you can filter nearly all events on application work. For our purpose, the filtered events can be divided into several groups:

  • Control over launching the processes and switching the process into debug mode
  • Filtering the network activity
  • Filtering the work with file system
  • Filtering the access to I/O Kit devices
  • Filtering the work with pipes
  • Filtering the access to Mach ports
  • Filtering the access to synchronization primitives
  • Filtering some critical system functions, such as mprotect, setaudit, setlcid, wait
  • Filtering the access to System V message queue

Though not comprehensive, this list covers most TrustedBSD functions and gives an idea of the framework capabilities.

Now we can go to our practical part and implement a TrustedBSD extension with access control to certain files. Creating this extension does not require any special knowledge or skills; you can just create a template driver with Xcode (TrustedBSD extensions are just kext modules) and implement a couple of handlers.

This article does not cover issues related to creating, loading, and unloading drivers for Mac OS X or providing interaction between the driver and user space. If these issues are new for you, read Kernel Extension Programming Topics — a great introduction to driver development for Mac OS X available at developers.apple.com website.

The work with TrustedBSD framework is very simple. The point is that only two functions are available to the developer: mac_policy_register and mac_policy_unregister which are responsible for registering and deregistering the policies, respectively.

You should fill two following structures for registering the policies: mac_policy_conf and mac_policy_ops. The mac_policy_ops structure contains the pointers to user callback functions used for handling occuring events. The mac_policy_conf structure contains the policy configuration information: policy name, pointer to the mac_policy_conf structure, flags for unloading the policy, etc.

The most convenient way is to initialize the policies from driver entry point and deregister them upon completion of the driver’s work. So, the initialization code in this particular example will be quite simple.

// Filling the structure with the pointers to callback functions.
static struct mac_policy_ops mac_ops ={
.mpo_policy_initbsd = mac_policy_initbsd, // Policy initialization
.mpo_vnode_check_open = mac_policy_open, // Policy initialization
.mpo_vnode_check_unlink = mac_policy_unlink, // File deletion handler
};

// Filling the structure with information on our policy
static struct mac_policy_conf mac_policy_conf =
{
.mpc_name = "protect_demo",
.mpc_fullname = "Protect demo!",
.mpc_labelnames = NULL,
.mpc_labelname_count = 0,
.mpc_ops = &mac_ops,
.mpc_loadtime_flags = MPC_LOADTIME_FLAG_UNLOADOK, // The policy is UNLOADABLE!
.mpc_field_off = NULL, .mpc_runtime_flags = 0
};

// The pointer to the registered policy
// Necessary for deregistering the policy
static mac_policy_handle_t mac_handle; // Driver entry point
int PolicyKext_start(void * d)
{
// In case of successful execution of mac_policy_register function
// the pointer to the registered policy
// will be written to the mac_handle variable
// Registering the policy
return mac_policy_register(&mac_policy_conf, &mac_handle, d);
}

Deregistering the policy is even simplier

// Driver exit point.
int PolicyKext _stop()
{
return mac_policy_unregister(mac_handle); // Deregistering the policy
}

Please give more attention to the mac_policy_initbsd callback. This function is called after policy initilization and load. In our case, it is just a stub. If you need to put additional logic into mac_policy_initbsd, remember that when loading the policies at the OS startup, this function will be called before kernel_task process starting (the very first process; its PID is always 0). Calling “wait” function from mac_policy_initbsd is forbidden and you should do other calls more carefully.

We should also discuss such important question as unloading the policies. While development, the policies should certainly be unloadable, and after that, in final product version – not unloadable (with few exceptions). To make the policy unloadable, upon the policy registration you should set MPC_LOADTIME_FLAG_UNLOADOK value in the mpc_loadtime_flags field of the mac_policy_conf structure. To forbid unloading the policy, set this field to 0. Other flag values are also available, for more information please refer to the documentation.

Now, we’re in the home stretch and have to create the following handlers for events associated with file opening and deletion.

  • typedef int mpo_vnode_check_open_t(kauth_cred_t cred, struct vnode *vp, struct label *label, int acc_mode );
  • typedef int mpo_vnode_check_unlink_t(kauth_cred_t cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp);

In both cases, the most interesting thing is vnode – vp structure with information on the object to which access attempt is made. You can as well get a lot of useful information from kauth_cred_t structure, e.g. effective and real user IDs and user groups IDs.

For final step, i.e. getting information on the current process name, PID, and accessed file name, you will need the following functions:

  • proc_selfname – getting the name of the process attempting to access the object.
  • proc_selfpid – getting the PID of the process attempting to access the object.
  • vnode_getname – getting the name of the object to which an access attempt is made.

It’s time to make a sample implementation of the handlers described above:

static int is_file_accessible(struct vnode *vp)
{
const char *vname = NULL;
char cbuf[MAXCOMLEN+1];
int retvalue = 0;

if (vp == NULL) // In some cases, absence of information about the node is OK,
{ // so we allow running the function.
return (retvalue);
}
vname = vnode_getname(vp);
if(vname) // Node name is not empty
{
// and there is an attempt to access
// the antivirus launch configuration file
if(strcasecmp(vname, "com.apple.xprotectupdater.plist") == 0)
{
proc_selfname(cbuf, sizeof(cbuf));
// while the process attempting to access the file is not
// an anti-virus update system
if (strcasecmp(cbuf,"XProtectUpdater"))
{
retvalue = EPERM; // access should be blocked.
}
vnode_putname(vname); // Clearing the node name requested before
}
}
return(retvalue);
}

static int mac_policy_open(
kauth_cred_t cred,
struct vnode *vp, struct label *label,
int acc_mode)
{
return is_file_accessible(vp);
}

static int mac_policy_unlink(
kauth_cred_t cred,
struct vnode *dvp, struct label *dlabel, struct vnode *vp,
struct label *label, struct componentname *cnp)
{
return is_file_accessible(vp);
}

The above examples show that implementation of TrustedBSD extension is neither a complex nor tricky process, while such extension ensures an easy control of almost all aspects of process launch on the computer.

1 Comments Working with TrustedBSD in Mac OS X

  1. Pingback: » Monitoring Process Creation via the Kernel (Part I)

Leave a Reply