Implementing SMACK

Simplified Mandatory Access Control Kernel (SMACK) provides a simple solution for mandatory access control (MAC). MAC provides the ability for a centralized entity to set access policy for system resources.

Linux's default access control policy is governed by permission bits on system resources (files, directories, devices, etc.). Permission bits can be modified by the resource owner (process with the same user ID as the resource). The access control policy is at the discretion of the resource owner; this system is classified as DAC (discretionary access control). With DAC, policies are set in a distributed manner as there are often many system users, each setting the access policy for its own resources.

In contrast, MAC policies are set for all system resources by a centralized entity.

Linux's DAC has known weaknesses that can lead to security leaks. MAC is often used to overcome some of the short comings of DAC for systems that require a higher level of security.

SMACK isn't the only MAC solution available. Because it's a simple solution, it's not flexible enough to handle all use cases. For the majority of use cases, it will be easier to setup and maintain.

SMACK supplements Linux's DAC system. DAC permissions are checked first; if access is granted, SMACK permissions are then checked. Consequently, SMACK can only limit access, it can't grant access beyond DAC permissions.

SMACK uses 'labels' on resources (objects in SMACK terminology) and processes (subjects) to determine access. Labels on resources can only be set by a privileged process. A privileged process can only set it's own label but not labels of other processes.

There are a number of single character labels ("_", "^", "*", "?", "@") that have special meanings. SMACK restricts read/write/execute access based on the subject label and the object label according to the following rules:

  1. Any access requested by a task labelled "*" is denied.
  2. A read or execute access requested by a task labelled "^" is permitted.
  3. A read or execute access requested on an object labelled "_" is permitted.
  4. Any access requested on an object labelled "*" 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.

Rule 6 lets us use explicit rules through adding specific labels. Explicit rules define the access rights a subject label can have on an object label. Only privileged processes can set rules.

Privileged Processess

Privileged processes use the CAP_MAC_OVERRIDE capability. It's also possible to configure the system so the CAP_MAC_OVERRIDE is honoured only for processes with a specific label. This configuration allows the system to restrict root processes (have CAP_MAC_OVERRIDE) that don't have the proper SMACK label.

Assigning Labels

Use smack_SetMyLabel() to set the SMACK label for the calling process. The calling process must be a privileged process. Setting SMACK labels for other processes isn't possible.

To set the SMACK label for file system objects use smack_SetLabel(), again the calling process must be privileged.

Set Rules

Use smack_SetRule() to set an explicit SMACK rule that gives a specified subject access to a specified object.

Supervisor

SMACK policies are set by the Legato startup scripts, the Legato Installer, and the Legato Supervisor.

By default system files have the "_" SMACK label meaning everyone has read and execute access to them. The Legato startup scripts are responsible for setting SMACK labels for system files that require special permission handling (e.g., /dev/null file is given the label "*" by the start up scripts so the file is fully accessible to everyone. The Legato startup scripts also ensure the Legato Supervisor and Installer have the 'admin' SMACK label.

The Legato Installer sets SMACK labels for all app bundled files. The SMACK label for each app is unique to the app.

The Supervisor sets SMACK labels for framework daemons, processes for apps, sandbox directories and SMACK rules for IPC bindings.

Framework daemons are given the SMACK label "framework".

All processes are given the same SMACK label as their app. All app labels are unique.

SMACK rules are set so IPC bindings between apps work. Here's a code sample of rules to set if a client app needs to access a server app:

'clientAppLabel' rw 'serverAppLabel' // client has read-write access to server.
'serverAppLabel' rw 'clientAppLabel' // server has read-write access to client.

Sandboxed directories are given labels corresponding to the app's access rights to those directory. Generally, an app only has read and execute permission to its sandboxes /bin directory. Its properties look like this:

owner = root group = root DAC permissions = -----r-x SMACK label = 'AppLabelrx'

The Supervisor also sets up the SMACK rule so the app has the proper access to the directory:

'AppLabel' rx 'AppLabelrx'

App's directories are given different labels than the app itself so that if an IPC binding is present, the remote app has access to the local app but doesn't have direct access to the local app's files.

All bundled files within an app's sandbox are given the app's SMACK label. This supports passing file descriptors from one app to another. However, the file descriptor can't be passed onto a third app.

Limitations

Extended attributes used to store the SMACK label are available on all file systems we currently use with one key feature is missing: when a new file is created, the file should inherit the SMACK label of the creator. Because this feature is missing, our current implementation of SMACK has the following limitations:

  • Mqueue file system will always set new files to "_" label. This means we can't control access between apps that use MQueues.
  • Tmpfs always sets new files to "*" label. This means we can't totally control access to files created in sandboxes because sandboxes use tmpfs. It's only an issue when file descriptors for the created files are passed over IPC to another app. The other app can then pass that fd onto a third app and so on.
  • QMI sockets are currently set to "*" because some apps need to write to them. Ideally, the QMI socket file would be given a label such as "qmi" and a rule would be created to only allow access to the app that requires it. However, there currently isn't a way to specify this in the xdef file.