Solaris Trusted Extensions Developer's Guide

Chapter 2 Labels and Clearances

This chapter describes the Solaris Trusted Extensions APIs for performing basic label operations such as initializing labels, and comparing labels and clearances. This chapter also describes the APIs for accessing the label of a process.

For examples of how the Trusted Extensions APIs are used in the Solaris OS, see the Solaris source code. Go to the Open Solaris web site and click Source Browser in the left navigation bar. Use the Source Browser to search through the Solaris source code.

This chapter covers the following topics:

Chapter 3, Label Code Examples provides code examples for the programming interfaces that are described in this chapter.

Privileged Operations and Labels

When an operation can bypass or override the security policy, the operation requires special privileges in its effective set.

Privileges are added to the effective set programmatically or administratively in these ways:

The operation needs special privileges when translating binary labels and when upgrading or downgrading sensitivity labels.

Users and roles can run operations with special privileges. These privileges can be specified by using rights profiles. Applications can be written to run certain functions with certain privileges, as well. When you write an application that must assume special privileges, make sure that you enable the privilege only while running the function that needs it and that you remove the privilege when the function completes. This practice is referred to as privilege bracketing. For more information, see Solaris Security for Developers Guide.

Most applications do not use privileges to bypass access controls because the applications operate in one of the following ways:

If an application tries to access data at sensitivity labels other than the sensitivity label of its process and access is denied, the process needs privileges to gain access. Privileges enable an application to bypass MAC or DAC. For example, the file_dac_read, file_dac_write, and file_dac_search privileges bypass DAC. The file_upgrade_sl and file_downgrade_sl privileges bypass MAC. No matter how access is obtained, the application design must not compromise the classification of the data that is accessed.

When your application changes its own sensitivity label or the sensitivity label of another object, be sure to close all file descriptors. An open file descriptor might leak sensitive data to other processes.

Label APIs

This section describes the APIs that are available for basic label operations. To use these APIs, you must include the following header file:

#include <tsol/label.h>

The label APIs compile with the -ltsol library option.

The Trusted Extensions APIs include data types for the following:

The APIs for the following operations are described in this section:

Detecting a Trusted Extensions System

The is_system_labeled() routine is used to determine whether you are running on a Trusted Extensions system. The following routine description includes the prototype declaration for each routine:

int is_system_labeled(void);

The is_system_labeled() routine returns TRUE (1) if the Trusted Extensions software is installed and active. Otherwise, it returns FALSE (0).

See the is_system_labeled(3C) man page. For an example of this routine's use, see get_peer_label() Label-Aware Function.

You can also use these other interfaces to determine whether the system is labeled:

Accessing the Process Sensitivity Label

The getplabel() and ucred_getlabel() routines are used to access the sensitivity label of a process. The following routine descriptions include the prototype declaration for each routine:

int getplabel(m_label_t *label_p);

The getplabel() routine obtains the process label of the calling process.

See the getplabel(3TSOL) man page.

m_label_t *ucred_getlabel(const ucred_t *uc);

The ucred_getlabel() routine obtains the label in the credential of the remote process.

See the ucred_getlabel(3C) man page. For an example of this routine's use, see get_peer_label() Label-Aware Function.

Allocating and Freeing Memory for Labels

The m_label_alloc(), m_label_dup(), and m_label_free() routines are used to allocate and free memory for labels. The following routine descriptions include the prototype declaration for each routine:

m_label_t *m_label_alloc(const m_label_type_t label_type);

The m_label_alloc() routine allocates a label in an m_label_t data structure on the heap. Labels must be allocated before calling routines such as getlabel() and fgetlabel(). Some routines, such as str_to_label(), automatically allocate an m_label_t structure.

When you create a label by using the m_label_alloc() routine, you can set the label type to be a sensitivity label or a clearance label.

int m_label_dup(m_label_t **dst, const m_label_t *src);

The m_label_dup() routine duplicates a label.

void m_label_free(m_label_t *label);

The m_label_free() routine frees the memory that was allocated for a label.

When you allocate an m_label_t structure or when you call another routine that automatically allocates an m_label_t structure, you are responsible for freeing the allocated memory. The m_label_free() routine frees the allocated memory.

See the m_label(3TSOL) man page.

Obtaining and Setting the Label of a File

The setflabel() routine, the getlabel() system call, and the fgetlabel() system call are used to obtain and set the label of a file. The following descriptions include the prototype declarations for the routine and the system calls:

int setflabel(const char *path, const m_label_t *label_p);

The setflabel() routine changes the sensitivity label of a file. When the sensitivity label of a file changes, the file is moved to a zone that corresponds to the new label. The file is moved to a new path name that is relative to the root of the other zone.

See the setflabel(3TSOL) man page.

For example, if you use the setflabel() routine to change the label of the file /zone/internal/documents/designdoc.odt from INTERNAL to RESTRICTED, the new path of the file will be /zone/restricted/documents/designdoc.odt. Note that if the destination directory does not exist, the file is not moved.

When you change the sensitivity label of a file, the original file is deleted. The only exception occurs when the source and destination file systems are loopback-mounted from the same underlying file system. In this case, the file is renamed.

When a process creates an object, the object inherits the sensitivity label of its calling process. The setflabel() routine programmatically sets the sensitivity label of a file system object.

The File Browser application and the setlabel command permit an authorized user to move an existing file to a different sensitivity label. See the setlabel(1) man page.

int getlabel(const char *path, m_label_t *label_p);

The getlabel() system call obtains the label of a file that is specified by path. The label is stored in an m_label_t structure that you allocate.

See the getlabel(2) man page.

int fgetlabel(int fd, m_label_t *label_p);

The fgetlabel() system call obtains the label of an open file by specifying a file descriptor.

When you allocate an m_label_t structure, you are responsible for freeing the allocated memory by using the m_label_free() routine. See the m_label(3TSOL) man page.

Obtaining Label Ranges

The getuserrange() and getdevicerange() routines are used to obtain the label range of a user and a device, respectively. The following routine descriptions include the prototype declaration for each routine:

m_range_t *getuserrange(const char *username);

The getuserrange() routine obtains the label range of the specified user. The lower bound in the range is used as the initial workspace label when a user logs in to a multilevel desktop. The upper bound, or clearance, is used as an upper limit to the available labels that a user can assign to labeled workspaces.

The default value for a user's label range is specified in the label_encodings file. The value can be overridden by the user_attr file.

See the setflabel(3TSOL), label_encodings(4), and user_attr(4) man pages.

bl_range_t *getdevicerange(const char *device);

The getdevicerange() routine obtains the label range of a user-allocatable device. If no label range is specified for the device, the default range has an upper bound of ADMIN_HIGH and a lower bound of ADMIN_LOW.

You can use the list_devices command to show the label range for a device.

See the list_devices(1) and getdevicerange(3TSOL) man pages.

Accessing Labels in Zones

These functions obtain label information from objects in zones. The following routine descriptions include the prototype declaration for each routine:

char *getpathbylabel(const char *path, char *resolved_path, size_t bufsize, const m_label_t *sl);

The getpathbylabel() routine expands all symbolic links and resolves references to /./, /../, removes extra slash (/) characters, and stores the zone path name in the buffer named by resolved_path. The bufsize variable specifies the size in bytes of this buffer. The resulting path does not have any symbolic link components or any /./, /../. This function can only be called from the global zone.

The zone path name is relative to the sensitivity label, sl. To specify a sensitivity label for a zone name that does not exist, the process must assert either the priv_file_upgrade_sl or the priv_file_downgrade_sl privilege, depending on whether the specified sensitivity label dominates or does not dominate the process sensitivity label.

See the getpathbylabel(3TSOL) man page.

m_label_t *getzoneidbylabel(const m_label_t *label);

The getzoneidbylabel() routine returns the zone ID of the zone whose label is label. This routine requires that the specified zone's state is at least ZONE_IS_READY. The zone of the calling process must dominate the specified zone's label, or the calling process must be in the global zone.

See the getzoneidbylabel(3TSOL) man page.

m_label_t *getzonelabelbyid(zoneid_t zoneid);

The getzonelabelbyid() routine returns the MAC label of zoneid. This routine requires that the specified zone's state is at least ZONE_IS_READY. The zone of the calling process must dominate the specified zone's label, or the calling process must be in the global zone.

See the getzonelabelbyid(3TSOL) man page.

m_label_t *getzonelabelbyname(const char *zonename);

The getzonelabelbyname() routine returns the MAC label of the zone whose name is zonename. This routine requires that the specified zone's state is at least ZONE_IS_READY. The zone of the calling process must dominate the specified zone's label, or the calling process must be in the global zone.

See the getzonelabelbyname(3TSOL) man page.

m_label_t *getzonerootbyid(zoneid_t zoneid);

The getzonerootbyid() routine returns the root path name of zoneid. This routine requires that the specified zone's state is at least ZONE_IS_READY. The zone of the calling process must dominate the specified zone's label, or the calling process must be in the global zone. The returned path name is relative to the root path of the caller's zone.

See the getzonerootbyid(3TSOL) man page.

m_label_t *getzonerootbylabel(const m_label_t *label);

The getzonerootbylabel() routine returns the root path name of the zone whose label is label. This routine requires that the specified zone's state is at least ZONE_IS_READY. The zone of the calling process must dominate the specified zone's label, or the calling process must be in the global zone. The returned path name is relative to the root path of the caller's zone.

See the getzonerootbylabel(3TSOL) man page.

m_label_t *getzonerootbyname(const char *zonename);

The getzonerootbyname() routine returns the root path name of zonename. This routine requires that the specified zone's state is at least ZONE_IS_READY. The zone of the calling process must dominate the specified zone's label, or the calling process must be in the global zone. The returned path name is relative to the root path of the caller's zone.

See the getzonerootbyname(3TSOL) man page.

Obtaining the Remote Host Type

This routine determines the remote host type. The following routine description includes the prototype declaration:

tsol_host_type_t tsol_getrhtype(char *hostname);

The tsol_getrhtype() routine queries the kernel-level network information to determine the host type that is associated with the specified host name. hostname can be a regular host name, an IP address, or a network wildcard address. The returned value is one of the enumerated types that is defined in the tsol_host_type_t structure. Currently, these types are UNLABELED and SUN_CIPSO.

See the tsol_getrhtype(3TSOL) man page.

Translating Between Labels and Strings

The label_to_str() and str_to_label() routines are used to translate between labels and strings. The following routine descriptions include the prototype declaration for each routine:

int label_to_str(const m_label_t *label, char **string, const m_label_str_t conversion_type, uint_t flags);

The label_to_str() routine translates a label, m_label_t, to a string. You can use this routine to translate a label into a string that hides the classification name. This format is suitable for storing in public objects. The calling process must dominate the label to be translated, or the process must have the sys_trans_label privilege.

See the label_to_str(3TSOL) man page.

The label_to_str() routine allocates memory for the translated string. The caller must free this memory by calling the free() routine.

See the free(3C) man page.

int str_to_label(const char *string, m_label_t **label, const m_label_type_t label_type, uint_t flags, int *error);

The str_to_label() routine translates a label string to a label, m_label_t. When you allocate an m_label_t structure, you must free the allocated memory by using the m_label_free() routine.

When you create a label by using the str_to_label() routine, you can set the label type to be a sensitivity label or a clearance label.

See the str_to_label(3TSOL) and m_label(3TSOL) man pages.

Readable Versions of Labels

The label_to_str() routine provides readable versions of labels. The M_LABEL conversion type returns a string that is classified at that label. The M_INTERNAL conversion type returns a string that is unclassified. The classified string version is typically used for displays, as in windows. The classified string might not be suitable for storage. Several conversion types are offered for printing purposes. All printing types show a readable string that is classified at the label that the string shows.

The conversion_type parameter controls the type of label conversion. The following are valid values for conversion_type, although not all types of conversion are valid for both level types:

Label Encodings File

The label_to_str() routine uses the label definitions in the label_encodings file. The encodings file is a text file that is maintained by the security administrator. The file contains site-specific label definitions and constraints. This file is kept in /etc/security/tsol/label_encodings. For information about the label_encodings file, see Solaris Trusted Extensions Label Administration, Compartmented Mode Workstation Labeling: Encodings Format, and the label_encodings(4) man page.

Comparing Labels

The blequal(), bldominates(), and blstrictdom() routines are used to compare labels. The blinrange() routine is used to determine whether a label is within a specified label range. In these routines, a level refers to a classification and a set of compartments in a sensitivity label or in a clearance label.

int blequal(const blevel_t *level1, const blevel_t *level2);

The blequal() routine compares two labels to determine whether level1 equals level2.

int bldominates(const m_label_t *level1, const m_label_t *level2);

The bldominates() routine compares two labels to determine whether level1 dominates level2.

int blstrictdom(const m_label_t *level1, const m_label_t *level2);

The blstrictdom() routine compares two labels to determine whether level1 strictly dominates level2.

int blinrange(const m_label_t *level, const brange_t *range);

The blinrange() routine determines whether the label, level, is within the specified range, range.

These routines return a nonzero value when the comparison is true and a value of 0 when the comparison is false. For more information about these routines, see the blcompare(3TSOL) man page. For examples of how these routines are used in the multilevel printing application, see Validating the Label Request Against the Printer's Label Range.

For more information about label relationships, see Label Relationships.

The blmaximum() and blminimum() routines are used to determine the upper and lower bounds of the specified label range.

void blmaximum(m_label_t *maximum_label, const m_label_t *bounding_label);

The blmaximum() routine compares two labels to find the least upper bound of the range. The least upper bound is the lower of two clearances, which is used to determine whether you have access to a system of a particular clearance.

For instance, use this routine to determine the label to use when creating a new labeled object that combines information from two other labeled objects. The label of the new object will dominate both of the original labeled objects.

See the blminmax(3TSOL) man page.

void blminimum(m_label_t *minimum_label, const m_label_t *bounding_label);

The blminimum() routine compares two labels to find the label that represents the greatest lower bound of the range that is bounded by the two levels. The greatest lower bound is the higher of two labels, which is also used to determine whether you have access to a system of a particular clearance.

See the blminmax(3TSOL) man page.

Acquiring a Sensitivity Label

Sensitivity labels are acquired from labeled zones and from other processes. A user can start a process only at the current sensitivity label of the current zone.

When a process creates an object, the object inherits the sensitivity label of its calling process. You can use the setlabel command or the setflabel() routine to set the sensitivity label of a file system object. See the setlabel(1) and setflabel(3TSOL) man pages.

The following script, runwlabel, runs a program that you specify in the labeled zone that you specify. You must run this script from the global zone.


Example 2–1 runwlabel Script

The runwlabel script must first acquire the sensitivity label of the labeled zone in which you want to run the specified program. This script uses the getzonepath command to obtain the zone path from the label that you specify on the command line. See the getzonepath(1) man page.

Next, the runwlabel script uses the zoneadm command to find the zone name associated with the zone path, which was acquired by the getzonepath command. See the zoneadm(1M) man page.

Finally, the runwlabel script uses the zlogin command to run the program that you specify in the zone associated with the label you specified. See the zlogin(1) man page.

To run the zonename command in the zone associated with the Confidential: Internal Use Only label, run the runwlabel script from the global zone. For example:


machine1% runwlabel "Confidential : Internal Use Only" zonename

The following shows the source of the runwlabel script:

#!/sbin/sh
#
# Usage:
# runwlabel "my-label" my-program
#
[ ! -x /usr/sbin/zoneadm ] && exit 0    # SUNWzoneu not installed

PATH=/usr/sbin:/usr/bin; export PATH

# Get the zone path associated with the "my-label" zone
# Remove the trailing "/root"
zonepath=`getzonepath "$1" | sed -e 's/\/root$//'`
progname="$2"

# Find the zone name that is associated with this zone path
for zone in `zoneadm list -pi | nawk -F: -v zonepath=${zonepath} '{
		if ($4 == zonepath) {
			print $2
		}
	}'`; do

		# Run the specified command in the matching zone
		zlogin ${zone} ${progname}
	done
exit

The following script, runinzone, runs a program in a zone that you specify even if the zone is not booted. You must run this script from the global zone.


Example 2–2 runinzone Script

The script first boots the zone you specified, and then it uses the zlogin command to run the waitforzone script in the specified zone.

The waitforzone script waits for the local zone automounter to come up, and then it runs the program you specified as the user you specified.

To run the /usr/bin/xclock command in the public zone, run the following from the global zone:


machine1% runinzone public terry /usr/bin/xclock

The following shows the source of the runinzone script:

#!/sbin/ksh
zonename=$1
user=$2
program=$3

# Boot the specified zone
zoneadm -z ${zonename} boot

# Run the command in the specified zone
zlogin ${zonename} /bin/demo/waitforzone ${user} ${program} ${DISPLAY}

The runinzone script calls the following script, waitforzone:

#!/bin/ksh
user=$1
program=$2
display=$3

# Wait for the local zone automounter to come up
# by checking for the auto_home trigger being loaded

while [ ! -d /home/${user} ]; do
sleep 1
done

# Now, run the command you specified as the specified user

su - ${user} -c "${program} -display ${display}"