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.
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:
If the executable file is owned by root and has the set user ID permission bit set, it starts with all privileges in its effective set. For example, the File Browser starts with all privileges in its effective set. Then, File Browser programmatically relinquishes most of its privileges to retain only the ones it needs to perform drag-and-drop operations across labels.
The administrator can specify privileges in manifest files for SMF services or in the RBAC database exec_attr file for general commands. For more information about this file, see the exec_attr(4) man page.
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.
Translating binary labels – You can translate a label between its internal representation and a string. If the label being translated is not dominated by the label of the process, the calling process requires the sys_trans_label privilege to perform the translation.
Upgrading or downgrading sensitivity labels – You can downgrade or upgrade the sensitivity label on a file. If the file is not owned by the calling process, the calling process requires the file_owner privilege in its effective set. For more information, see the setflabel(3TSOL) man page.
A process can set the sensitivity label on a file system object to a new sensitivity label that does not dominate the object's existing sensitivity label with the file_downgrade_sl privilege in its effective set. The file_downgrade_sl privilege also allows a file to be relabeled to a disjoint label.
A process can set the sensitivity label on a file system object to a new sensitivity label that dominates the object's existing sensitivity label with the file_upgrade_sl privilege in its effective set.
Most applications do not use privileges to bypass access controls because the applications operate in one of the following ways:
The application is launched at one sensitivity label and accesses data in objects at that same sensitivity label.
The application is launched at one sensitivity label and accesses data in objects at other sensitivity labels, but the mandatory access operations are permitted by the system security policy. For example, read-down is allowed by MAC.
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.
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:
Sensitivity label – The m_label_t type definition represents a sensitivity label. The m_label_t structure is opaque.
Interfaces accept a variable of type m_label_t as a parameter. Interfaces can return sensitivity labels in a variable of type m_label_t. The m_label_t type definition is compatible with the blevel_t structure.
Sensitivity label range – The brange_t data structure represents a range of sensitivity labels. The structure holds a minimum label and a maximum label. The structure fields are referred to as variable.lower_bound and variable.upper_bound.
The APIs for the following operations are described in this section:
Detecting a Trusted Extensions system
Accessing the process sensitivity label
Allocating and freeing memory for labels
Obtaining and setting the label of a file
Obtaining label ranges
Accessing labels in zones
Obtaining the remote host type
Translating between labels and strings
Comparing labels
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:
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:
X client. If you are writing an X client that depends on multilevel functionality, use the XQueryExtension() routine to query the X server for the SUN_TSOL extension.
Shell script. If you are writing a shell script that will determine whether the system is labeled, use the plabel command. See the plabel(1) man page.
The following example shows the smf_is_system_labeled() function used by the /onnv/onnv-gate/usr/src/cmd/svc/shell/smf_include.sh script:
# # Returns zero (success) if system is labeled (aka Trusted Extensions). # 1 otherwise. # smf_is_system_labeled() { [ ! -x /bin/plabel ] && return 1 /bin/plabel > /dev/null 2>&1 return $? }
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:
The getplabel() routine obtains the process label of the calling process.
See the getplabel(3TSOL) man page.
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.
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:
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.
The m_label_dup() routine duplicates a 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.
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:
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.
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.
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.
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:
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.
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.
These functions obtain label information from objects in zones. The following routine descriptions include the prototype declaration for each routine:
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.
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.
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.
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.
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.
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.
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.
This routine determines the remote host type. The following routine description includes the prototype declaration:
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.
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:
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.
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.
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:
M_LABEL is a string of the label that is based on the type of label: sensitivity or clearance. This label string is classified at the level of the label and is therefore not safe for storing in a public object. For example, an M_LABEL string such as CONFIDENTIAL is not safe for storing in a public directory because the words in the label are often classified.
M_INTERNAL is a string of an unclassified representation of the label. This string is safe for storing in a public object. For example, an M_INTERNAL string such as 0x0002-04-48 is safe for storing in an LDAP database.
M_COLOR is a string that represents the color that the security administrator has associated with the label. The association between the label and the color is stored in the LOCAL DEFINITIONS section of the label_encodings file.
PRINTER_TOP_BOTTOM is a string used as the top and the bottom label of banner and trailer pages.
PRINTER_LABEL is a string used as the downgrade warning on the banner page.
PRINTER_CAVEATS is a string used in the caveats section on the banner page.
PRINTER_CHANNEL is a string used as the handling channels on the banner page.
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.
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.
The blequal() routine compares two labels to determine whether level1 equals level2.
The bldominates() routine compares two labels to determine whether level1 dominates level2.
The blstrictdom() routine compares two labels to determine whether level1 strictly dominates level2.
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.
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.
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.
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.
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.
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}"