Using security policies

Updated: April 19, 2023

QNX Neutrino security policies use a security policy file to define the rules that grant privileges to processes and the security policy types used to apply the rules to processes and other system elements.

For convenience and efficiency, QNX Neutrino provides the secpolgenerate utility, which completes most of the work required to create a security policy for the current system, as well as other tools that manage and monitor behavior related to security policies. For a detailed description of how to use secpolgenerate to develop and modify security policies, see Tutorial: Build a system that uses a security policy.”

Creating and applying a security policy

The following workflow illustrates the security policy mechanism in brief.

  1. At system build time, create a file (for example, policy.txt) and add content to it that defines security policy types and the privileges they have. You can use secpolgenerate to speed up the creation process. Some example security policy file content:
    # Type that represents the random process
    type random_t;
    # Type to use for the sshd daemon (a client of random)
    type sshd_t;
            
    allow_attach random_t /dev/random;
    allow sshd_t random_t : channel connect;
  2. Compile the policy using the secpolcompile utility. For example:
    secpolcompile -o secpol.bin policy.txt
  3. Include the file in your IFS as /proc/boot/secpol.bin.
  4. At boot time, use the secpolpush utility to push policy content to procnto.
  5. Apply the corresponding policy rules by specifying the appropriate security type when you run a process. For example:
    on -T random_t random -t -p -U 22:22
There is no default security policy.

What a policy controls

A system's security policy controls the following privileges:

Types

Security policy rules use security types to identify processes and the objects that they interact with. In the security policy, the types are represented by names.

For example, the following two rules grant privileges to a process that has the type random_t (most likely the random service). The second rule grants a privilege to a process of type random_t when it interacts with a channel with type devb_t:

allow_attach random_t /dev/random;
allow random_t devb_t : channel connect;

The type devb_t could also be used for a process and grant it privileges via rules. However, because objects such as channels don’t have privileges, those privileges would only apply to processes with that type.

When you compile a policy, the types in the policy are assigned an integer value that is an alternate way to identify a security type. Numbering starts at 0 and increases by one for each type that’s declared.

When you start a process with a type, the system restricts it to the actions given to it by that type's rules (e.g., random started as random_t).

A process can change the rules that apply to it by changing its type (using secpol_transition_type()) and a process can spawn processes as a type other than its own type (using secpol_posix_spawnattr_settypeid()).
Note: To avoid weakening security by giving a process permissions it does not need, make sure each resource manager has its own type (i.e., two or more resource managers should not share a type).

Specifying types in a security policy

Type names follow the same rules as C identifiers. They can contain alphanumeric characters and the underscore, but must not start with a digit.

All the types that the system requires have to be declared in the policy. Any mistyped names are detected as errors at compilation time.

Specifying types in code

Because the type identifiers assigned to types are not fixed, code that uses types should look up the name to convert to an identifier rather than embed a number (using secpol_get_type_id()).

Assigning types to processes

Processes started from the ifs start-up script are not automatically fully controlled by a security policy. Although they appear to have a type ID of 0 and are affected by some parts of a policy, they only have the set of abilities that QNX Neutrino assigns by default.

During the startup process, transient commands such as mount can be run without explicit types but anything that runs beyond startup should not. To start processes with explicit types, QNX Neutrino provides the on utility and slm service, though you can also do it via custom services or utilities by using the type ID attribute in calls to posix_spawn().

After a process is started with an explicit type, it is fully controlled by a security policy and any child processes that it creates inherit the parent's type (unless the process explicitly changes it or the policy dictates that type should change). Thus, by making sure that all processes that run past startup have an explicit type, you ensure that the system is fully controlled by the policy, and only processes that the policy fully controls can be created.

To verify that there are no processes running without an explicit type, run pidin -f a_n after startup and check the output to make sure that all processes have a security type.

Processes can change their own type by calling secpol_transition_type() and set the type of processes they spawn by using secpol_posixattr_settypeid(). However, the ability to change their own type or that of their children is controlled by an ability and thus controlled by the policy.

Assigning types to objects

When a policy determines whether an action can be taken, it considers the type of the process that takes the action and the object it acts on.

Currently, the only object that is provided by QNX Neutrino is a channel. The type assigned to a channel determines the types that other processes must have to connect to it.

By default, channels have the type of the process that creates them. In addition, a security policy can specify which type a channel takes when a process of a specified type calls resmgr_attach() for a specific path. For more information, see Rules for connecting to channels.”

You can add objects that take types as required by defining the object class and the associated actions in the security policy and then referencing them in policy rules. For an example of using a custom class that represents an acted-on object (a mountpoint), see “Permission checking by operation and object acted on” in Customizing permissions using a security policy.”

Controlling type changes

Use the PROCMGR_AID_SETTYPEID ability to:

Usually if a process is granted the PROCMGR_AID_SETTYPEID ability, it should be granted only a limited subrange since a process can gain the privileges of any type it is allowed to switch to or spawn processes with.

Security policies and Qnet

The way type identifiers are numbered becomes important only when you use Qnet and a different security policy between nodes. To ensure the correspondence of type identifiers on each nodes, ensure that any types that must be used across nodes are defined in a single file that is compiled into all policies prior to any other type definitions.