Customizing permissions using a security policy

Updated: April 19, 2023

The secpol_get_permission() and secpol_check_permission() functions allow you to support fine-grained permission checking in a system that uses security policies.

You can use secpol_check_permission() in one of the following two ways, which depend on how the function's otype (object type) parameter is used:

Permission checking by operation

To check a permission based on both the type of the requester and the operation being performed, set otype to the type ID of the resource manager itself (obtained by calling secpol_type_id()).

For example, while currently QNX systems use a single ability to control mounting a filesystem, it could be changed to use security policy permissions instead. The following security policy entries define a client who can only mount and unmount filesystems (fs_client_t) and a super client who can perform all operations (fs_super_client_t):

class fs_operation { mount unmount suid trusted before after };

allow fs_client_t filesystem_t:fs_operation {
    mount unmount 
};
allow fs_super_client_t filesystem_t:fs_operation {
    mount unmount suid trusted before after 
};

This configuration does not restrict permissions based on the object that the operation is performed on. If the resource manager supports multiple filesystems, the same restrictions apply to all of them.

The filesystem resource manager runs with a type of filesystem_t. To get the permission handles that secpol_check_permission() requires, it calls secpol_get_permission() with the appropriate class and permission. For example:

s_mount_perm = secpol_get_permission(NULL, "fs_operation", "mount", 0);

The following code example illustrates how the filesystem can check permissions for a mount request:

if ( secpol_check_permission(&ctp->info, secpol_type_id(), s_mount_perm) != 0 ) {
    return EPERM;
}

Permission checking by operation and object acted on

You can extend the fine-grained security illustrated in the previous example to additionally check the object being acted on (i.e., the mountpoint).

For example, a system has filesystems mounted in several places. One mountpoint must be carefully protected because it is used to store binaries and other files for system services. A second mountpoint is used to mount user-supplied USB thumb drives. Although it is likely not important to control who can mount things on the second mountpoint, any filesystems mounted there should not honor setuid bits (which would allow a binary to execute with a user ID equal to the owner of the file, including executing as root).

The security file to support this example defines system_fs_t and user_fs_t to represent the two mountpoints:

type system_fs_t;
type user_fs_t;
        
class fs_operation {
    mount unmount suid trusted before after
};

allow {
    fs_client_t fs_super_client_t
} user_fs_t:fs_operation {
    mount unmount
};
allow fs_super_client_t  system_fs_t:fs_operation {
    mount unmount suid trusted before after
};

Both regular and super clients can perform mount and unmount operations on the mountpoint for the USB drive (configured to be treated as type user_fs_t) but any setuid bit is disregarded. The super client can perform any operation on the system mountpoint (configured to be treated as system_fs_t).

The filesystem resource manager (again running as filesystem_t) uses the following calls to look up the types that represent the two mountpoints:

s_system_mp_type = secpol_get_type_id(NULL, "system_fs_t");
s_user_mp_type = secpol_get_type_id(NULL, "user_fs_t");

The following calls provide the filesystem resource manager with the permission handles for secpol_check_permission() (same as the first example):

s_mount_perm = secpol_get_permission(NULL, "fs_operation", "mount", 0);

The resource manager can check permissions for a request to mount the system mountpoint using the following code:

if ( secpol_check_permission(&ctp->info, s_system_mp_type, s_mount_perm) != 0 ) {
    return EPERM;
}

Similarly, it can check permissions for a request to mount the user mountpoint using the following code:

if ( secpol_check_permission(&ctp->info, s_user_mp_type, s_mount_perm) != 0 ) {
    return EPERM;
}