Privileges organize security-related powers into discrete pieces where each piece (or privilege) maps to a single security-related task. Privileges enable a program to perform specific tasks normally prohibited by the system security policy. Prohibited tasks are such things as accessing a file or directory to which the program does not have the appropriate mandatory or discretionary access.
A program turns on (makes effective) one or more privileges to perform one security-related task. For example, if the program does not have mandatory write access to a file, it turns on the file_mac_write privilege. If the program does not have discretionary write access either, it also turns on the file_dac_write privilege. However, if the program has both mandatory and discretionary write access, it needs no privileges. Most programs do not use privileges because they operate within the bounds of the system security policy.
This chapter describes the programming interfaces for handling privileges.
The Trusted Solaris environment allows up to 128 different privileges. The total includes the following types of Trusted Solaris privileges and site-defined privileges. See priv_desc(4) for a description of the Trusted Solaris privileges.
File system privileges override file system restrictions on user and group IDs, access permissions, labeling, ownership, and file privilege sets.
System V Interprocess Communication (IPC) privileges override restrictions on message queues, semaphore sets, or shared memory regions.
Network privileges override restrictions on reserved port binding, multilevel port binding, sending broadcast messages, or specifying security attributes on messages or communication endpoints.
Process privileges override restrictions on process auditing, labeling, covert channel delays, ownership, clearance, user IDs, or group IDs.
System privileges override restrictions on system auditing, workstation booting, workstation configuration management, console output redirection, device management, file systems, creating hard links to directories, increasing message queue size, increasing processes, workstation network configuration, third-party loadable modules, or label translation.
X Window System privileges override restrictions on reading to and writing from windows, input devices, labeling, font paths, moving data between windows, X server resource management, or direct graphics access (DGA).
Privileges are organized into file privilege sets and process privilege sets.
Executable files, interpreted files, and CDE actions have file privilege sets assigned through the File Manager, with setfpriv(1), or by another privileged program. The file privilege sets are the forced set and the allowed set.
The allowed set contains the privileges that will be assigned to the executable file (forced file set) or inherited and used by the executing process. When a process inherits a privilege from another process, it cannot use that privilege unless the privilege is in the allowed set of its executable file.
Allowed privileges provide Trojan horse protection because they protect against an untrusted process entering the system and inheriting privileges from another process. See "Inheritable Set" for more information on inheriting privileges.
The forced set contains the privileges a program must have when it begins execution for security-related tasks performed by any user. Commands with forced privileges can be invoked from any shell, and CDE actions with forced privileges can be invoked from any workspace. The forced set must always be equal to or a subset of the allowed set, and so, every privilege in the forced set is also in the allowed set.
Interpreted files are scripts that begin with #! and go through an interpreter to be executed. The script file can have forced and allowed privilege sets and the interpreter can have forced and allowed privilege sets. The final forced set is the combination of the forced set assigned to the script and the forced set assigned to the interpreter restricted by the allowed set of the interpreter. The allowed set of the script does not restrict the final forced set.
Executing processes have process privilege sets computed from algorithms based on the contents of the file sets and any privileges inherited from the calling process. The process privilege sets are the inheritable, saved, permitted, and effective sets.
The inheritable set contains the privileges (if any) received from the parent process. A process passes its inheritable set to a new program during an exec(1) or a new process during a fork(2). The inheritable set of the new program or process always equals the inheritable set of the calling process. The new process or program can use only those inherited privileges that are also in the allowed set of its executable file, but passes all inheritable privileges to a new program or process. A program can clear its inheritable set and add any privileges in its permitted set to the inheritable set prior to a fork() or exec().
The system administrator can assign an inheritable set to a CDE action or command in an execution profile. The privileges are inherited when the user or role to which the execution profile is assigned starts the CDE action or executes a command from the profile shell.
If a forced privilege is in the process's permitted set, that process can set the forced privilege in its own inheritable set and pass the forced privilege to a new process or program.
The saved set is a copy of the inherited privileges the process is allowed to use. The saved set equals the inheritable set restricted by the allowed set. Those privileges in the inheritable set also in the allowed set are put in the saved set. There are no interfaces for changing the saved set.
A program can query its saved set to determine the origination of a privilege. If the privilege is in the saved set, it is inherited for the current program invocation. If the privilege is not in the saved set, it is forced for the current program invocation.
A process may take a more limited (workstation-wide) action on a security-related task when started by a normal user (forced privilege), and a wider (network-wide) action on the same security-related task when started by an authorized user in an administrative role (inherited privilege).
The permitted set contains the forced and inherited privileges a process can use. The permitted set is the forced set plus the inheritable set restricted by the allowed set. Those privileges in the inheritable set also in the allowed set are combined with the forced set and placed in the permitted set. A privileged process is a process with a permitted set not equal to zero.
Privileges can be removed from the permitted set, but not added. Once a permitted privilege is removed, it cannot be added back, it cannot be added to the inheritable set, and is removed from the inheritable set if it was added to the inheritable set prior to being removed from the permitted set.
As a security precaution, you can remove the privileges from the permitted set the program never uses. This way a program can never make use of an allowed privilege incorrectly assigned to its executable file or accidentally inherited.
Effective privileges are those permitted privileges a process uses for a single security-related task. By default, the effective set is initially equal to the permitted set, but a program should turn the effective set off at the beginning of execution to prepare for privilege bracketing.
Privilege bracketing is the practice of turning the effective privilege set off, then turning on (making effective) only those privileges needed for a specific security-related task, and turning them off as soon as they are no longer needed. See "Bracketing Effective Privileges".
Privilege-unaware programs change their UIDs either to gain or give up rights associated with the new UID. To simulate that action in a privilege-based system rather than a UID-based system, the effective and saved privilege sets are modified across setuid calls. If the setuid(2), setreuid(2), or seteuid(2) system call is called, the effective privilege set is copied to the saved set and the effective set is cleared. If you need the effective set, copy it back from the saved set or turn the effective privileges you need back on. If you need the original saved set (to determine the origination of a privilege), do the tests first or make a copy of the saved set.
The effective set is cleared based on the principle that a process cannot use privileges granted to the original caller while the user ID is changed. A setuid program can still manipulate privileges from the permitted set by putting them into the effective set. When a set UID program changes from its saved UID ID to the calling user ID, it gives up its privilege. When it changes back to the saved UID ID, it regains privilege.
Since set UID programs may not be aware of privileges, their privilege bracketing (see "Use Privilege Bracketing") is tracked in the privilege sets.
All privileged applications are part of the Trusted Computing Base (TCB). Some privileged applications have one or more forced privileges and might or might not inherit privileges. These applications are the Trusted Solaris equivalent of setuid applications in standard UNIX systems.
Other privileged applications have no forced privileges and always inherit privileges from the calling process. These applications are always called by a privileged process.
The priv_desc(4) man page lists privilege names, manifest constant names, and description text for all system privileges.
The system calls that get and set file privilege sets require mandatory access and discretionary access to the file and may require privilege if access is denied. See the fgetfpriv(2) man page for specific details.
The file_setpriv privilege is required to set file privilege sets with the setfpriv(1) and fsetfpriv(2) system calls.
When a process writes information to an executable file, the file_setpriv privilege is needed to prevent the file's forced and allowed privilege sets being set to none.
The proc_dumpcore privilege must be effective for a privileged process to create a core file because the core file from a privileged process is likely to contain sensitive information. If this privilege is not effective, the process will not create a core file when it dies. For debugging purposes (only), you could make this privilege effective at the beginning of execution and leave it effective until the process dies.
The calling process needs the proc_setid privilege in its effective set to change its user ID, group ID, or supplemental group ID.
Privileged applications should be developed in an isolated, protected environment separate from an operational Trusted Solaris system. Unfinished privileged applications are inherently untrustworthy and should not have an opportunity to compromise the security of a functioning system. The following additional practices are recommended for all privileged applications.
See Appendix B, Trusted Solaris Interfaces Reference for information on secure application packaging.
When an application uses privilege, system security policy is being breached. Privileged tasks should be bracketed and carefully controlled to ensure that sensitive information is not compromised. See "Bracketing Effective Privileges" for information on how to bracket privileges.
Shell escapes in an application can enable an end user to violate trust. For example, some mail applications interpret the !command line as a command and execute it. If a mail application is a trusted process, it runs with privileges. The end user can use this feature to create a script to take advantage of the mail application privileges. Applications should have this capability removed when they run in a trusted environment.
Running applications directly from the command line should be avoided if the application has been given privileges because the end user can take advantage of the privileges. For example, many application allow the end user to enter a command to execute followed by a document name. If the application has been given the privilege to override mandatory access controls (if the application needs to write down to an outside application), this could result in the end user opening a document that he or she does not ordinarily have the privileges to see.
Covert channels in privileged applications should be sought out and eliminated. A covert channel is an unintended path through which information can be transmitted in ways not protected by mandatory access controls. For example, in a privileged multilabel client/server application, the server has a queue of service requests. If unprivileged clients can add and remove requests from the queue and the queue has a finite size, the information on the full or not-full state of the queue can be exploited as a covert channel.
To use the programming interfaces described in this chapter, you need the following header file.
#include <tsol/priv.h>
The examples in this chapter compile with the following library:
-ltsol |
One privilege is represented by the priv_t type definition. You initialize a variable of type priv_t with a privilege ID that can be either the constant name or numeric ID. The constant name is preferred because it makes your code easier to read.
priv_t priv_id = PRIV_FILE_DAC_WRITE;
Privilege sets are represented by the priv_set_t data structure. You initialize variables of type priv_set_t with the str_to_priv_set(3TSOL) routine or the PRIV_ASSERT macro depending on whether you want to assert one privilege at a time using its privilege ID (PRIV_ASSERT) or convert a string of one or more privileges into a privilege set using a single interface (str_to_priv_set).
The type of file privilege set to be worked on is represented by the priv_ftype_t type definition. Values are PRIV_ALLOWED and PRIV_FORCED.
The type of process privilege set to be worked on is represented by the priv_ptype_t type definition. Values are PRIV_EFFECTIVE, PRIV_INHERITABLE, PRIV_PERMITTED, and PRIV_SAVED.
The type of operation performed on a file or process privilege set is represented by the priv_op_t type definition. Not all operations are valid for every type of privilege set. Read the privilege set descriptions in "Privilege Sets" for details.
Values are the following:
PRIV_ON - Turn the privileges asserted in the priv_set_t structure on in the specified file or process privilege set.
PRIV_OFF - Turn the privileges asserted in the priv_set_t structure off in the specified file or process privilege set.
PRIV_SET - Set the privileges in the specified file or process privilege set to the privileges asserted in the priv_set_t structure. If the structure is initialized to empty, PRIV_SET clears (sets to none) the privilege set.
The privilege macros operate on single privileges and privilege sets. They are described on the priv_macros(5) man page. The macros do not directly change the privilege sets associated with files or processes, but manipulate variables of type priv_set_t.
Privilege Macro |
Description |
---|---|
PRIV_ASSERT(priv_set, priv_id) |
Put the privilege (priv_id) into the set (priv_set). |
PRIV_ISASSERT(priv_set, priv_id) |
Return non-zero if the privilege (priv_id) is asserted in (priv_set). |
PRIV_EQUAL(priv_set_a, Priv_set_b) |
Return non-zero if the sets are identical. |
PRIV_EMPTY(priv_set) |
Initialize the set to empty. |
PRIV_FILL(priv_set) |
Fill the set with all privileges. |
PRIV_ISEMPTY(priv_set) |
Return non-zero if the set is empty, and 0 if not empty. |
PRIV_ISFULL(priv_set) |
Return non-zero if the privilege contains all privileges defined for the system, and 0 otherwise. |
PRIV_CLEAR(priv_set, priv_id) |
Remove the privilege (priv_id) from set (priv_set). |
PRIV_INTERSECT(priv_set_a, priv_set_b) |
Store the intersection of set_a and set_b in set_b. |
PRIV_INVERSE(priv_set) |
Stores the inverse of priv_set in priv_set. |
PRIV_UNION(priv_set_a, priv_set_b) |
Store the union of set_a and set_b in set_b. |
PRIV_XOR(priv_set_a, priv_set_b,) |
Store the exclusive or of set_a and set_b in set_b. |
PRIV_ISSUBSET(priv_set_a, priv_set_b) |
Returns non-zero when all privileges asserted in priv_set_a are also asserted in priv_set_b, and 0 otherwise. |
PRIV_TEST(priv_id, errno) |
Test whether priv_id is in the effective set, and sets errno to 1 if True and 0 if False. |
The following interfaces are available for handling file and process privilege sets. Where there is one set of interfaces to access a file using the pathname and another to access a file by the file descriptor, the examples use the pathname interfaces only because the syntax is almost identical.
These system calls get and set file and process privilege sets.
These system calls get and set the file privilege set using the full path name of the file. Refer to the getfpriv(2) man page.
int getfpriv( char *path, priv_ftype_t type, priv_set_t *priv_set); int setfpriv( char *path, priv_op_t op, priv_ftype_t type, priv_set_t *priv_set);
These system calls get and set file privilege set using a file descriptor. Refer to the getfpriv(2) man page.
int fgetfpriv(int fd, priv_ftype_t type, priv_set_t *priv_set); int fsetfpriv(int fd, priv_op_t op, priv_ftype_t type, priv_set_t *priv_set);
These system calls get and set process privilege sets. Refer to the getppriv(2) man page.
int getppriv(priv_ptype_t type, priv_set_t *priv_set); int setppriv(priv_op_t op, priv_ptype_t type, priv_set_t *priv_set);
You can also use the library routines below to access process privilege sets. The syntax is a little different, but the semantics are the same.
These library routines get process privilege sets, convert a privilege ID or privilege set between binary and text, and get the privilege description text for a specified privilege ID.
These library routines set the effective, permitted, and inheritable privilege sets on a process. Refer to the set_effective_priv(3TSOL) man page.
int set_effective_priv(priv_op_t op, int privno, priv_t priv_id); int set_permitted_priv(priv_op_t op, int privno, priv_t priv_id); int set_inheritable_priv(priv_op_t op, int privno, priv_t priv_id);
You can also use setppriv(2) and getppriv(2) to access process privilege sets. The syntax is a little different, but the semantics are the same.
These library routines translate a privilege ID or a privilege set between binary and text. Refer to the priv_to_str(3TSOL) man page.
char* priv_to_str(const priv_t priv_id); priv_t str_to_priv(const char *priv_name); char* priv_set_to_str(priv_set_t *priv_set, const char sep, char *buf, int *blen); char* str_to_priv_set(const char *priv_names, priv_set_t *priv_set, const char *sep);
These library routines get the privilege text for a specified privilege ID. Refer to the priv_to_str(3TSOL) man page.
char* get_priv_text(const priv_t priv_id);
These library routines convert the specified privilege ID to its corresponding external name or numeric ID and back. These routines read the privilege names database file described on the priv_name(4) man page to translate between the priv_id and *string.
In this example, priv_id is initialized to the manifest constant name PRIV_FILE_DAC_WRITE and passed to priv_to_str(3TSOL) routine to convert it to the external name.
The header files and declarations for the code segments in this section are provided in the first program.
#include <tsol/priv.h> main() { priv_t priv_id = PRIV_FILE_DAC_WRITE; char *string; string = priv_to_str(priv_id); printf("Priv string = %s\n", string); }
The printf statement prints the following:
Priv string = file_dac_write |
In the next example, the string returned from the priv_to_str(3TSOL) routine is passed to the str_to_priv(3TSOL) routine to convert the string to the numeric ID.
priv_id = str_to_priv(string); printf("Priv ID = %d\n", priv_id);
The printf statement prints the following:
Priv ID = 6 |
The get_priv_text(3TSOL) routine returns the description text for the specified priv_id. The priv_name(4) man page lists the description text for all privileges in the system.
string = get_priv_text(priv_id); printf("%s\n", string);
The printf statement prints the following:
Allows a process to write a file or directory whose permission bits or ACL do not allow the process write permission. |
The Trusted Solaris environment provides the user commands and programming interfaces described here for setting and getting the privilege sets of an executable file. If no forced and allowed privileges are set, by default the forced and allowed privilege sets contain none.
If you set file privilege sets prior to execution, the new privilege sets take effect immediately and are used to compute the process privilege sets for the current execution. If you set file privilege sets during execution, they do not take effect until the next execution and have no effect on the process privilege sets for the current execution.
To set and get the file privilege sets from the command line, use setfpriv(1) and getfpriv(1). The file_setpriv privilege is required with setfpriv(1) so this command must be executed from the profile shell with this privilege. See "Assigning File Privileges using a Script" for information on using setfpriv(1) in a script.
This command line sets the file privilege sets on executable for the examples in this chapter. When you specify more than one privilege, the names are separated by commas with no spaces. If you want to use spaces, enclose the privilege names in double quotes ("privilege1, privilege2").
phoenix% setfpriv -s -f file_setpriv \ -a file_mac_write,proc_setid,file_setpriv executable |
This command line produces output to verify the file privilege sets were set:
phoenix% getfpriv executable executable FORCED: file_setpriv ALLOWED: file_mac_write,file_setpriv,proc_setid |
The privilege macros and system calls described in this section get and set file privilege sets. The program below has the header files and variable declarations for the entire series of examples for this chapter. It also contains code to set and get the file privilege sets for execfile, which will be exec'd later to show what happens to process sets during an exec.
The setfpriv(1) system call sets the forced and allowed privilege sets on execfile and requires the file_setpriv privilege. The file_setpriv privilege is in the forced set for executable to make it available in the permitted set during execution. By default, the effective set equals the permitted set, and all effective privileges are on until explicitly turned off in preparation for privilege bracketing. The use of file_setpriv in this code does not follow security guidelines until privilege bracketing is put into effect as described in "Bracketing Effective Privileges".
/* cc priv.c -o executable -ltsol */ #include <tsol/priv.h> #include <sys/types.h> #include <errno.h> #include <stdio.h> /* Global Variables*/ extern int errno; char buffer [3*1024]; main() { char *priv_names = "file_mac_write,proc_setid"; char *string; char *privilege; char *file = "/export/home/zelda/executable"; char *execfile = "/export/home/zelda/execfile"; priv_set_t priv_set, priv_get, permitted_privs, saved_privs; int length = sizeof(buffer); int retval; pid_t pid; /* To use with exec() later */ char *argv[8] = {"execfile"}; /* Initialize privilege set data structures */ PRIV_EMPTY(&priv_get); PRIV_EMPTY(&priv_set); /* Turn allowed privileges off. See text for discussion. */ retval = setfpriv(execfile, PRIV_SET, PRIV_ALLOWED, &priv_get);
/* Assert the privileges in priv_names in a privilege set */ /* structure and assign to execfile. See text below for discussion */ /* on methods for asserting privileges */ if((string = str_to_priv_set(priv_names, &priv_set, ",")) != NULL) printf("string = %s errno = %d\n", string, errno); retval = setfpriv(execfile,PRIV_ON, PRIV_ALLOWED, &priv_set); /* Check that the allowed privilege set contains the privileges */ retval = getfpriv(execfile, PRIV_ALLOWED, &priv_get); priv_set_to_str(&priv_get, ',', buffer, &length); printf("execfile Allowed = %s\n", buffer); /* Initialize privilege set data structures */ PRIV_EMPTY(&priv_set); PRIV_EMPTY(&priv_get); /* Assert file_mac_write in a privilege set structure */ PRIV_ASSERT(&priv_set, PRIV_FILE_MAC_WRITE); /* Set the forced privilege set on execfile */ retval = setfpriv(execfile, PRIV_ON, PRIV_FORCED, &priv_set); /* Check that the forced privilege set contains the privilege */ retval = getfpriv(execfile, PRIV_FORCED, &priv_get); priv_set_to_str(&priv_get, `,', buffer, &length); printf("execfile Forced =%s\n", buffer); }
The printf statements print the file privilege sets for execfile as follows:
execfile Allowed = file_mac_write,proc_setid execfile Forced = file_mac_write |
The output uses a comma (",") to separate the allowed privileges. The separator is specified in the calls to priv_set_to_str(3TSOL). The separator is not used when there is only one privilege in the set.
The forced set is a subset of the allowed set. Any privileges in the forced set are cleared when the allowed set is cleared. The allowed set is none by default, but it is a good practice to clear it first so you know you are starting from zero. Always clear and set the allowed set before you set the forced set. After the following code executes, the allowed and forced sets are both none.
PRIV_EMPTY(&priv_set); retval = setfpriv(execfile, PRIV_SET, PRIV_ALLOWED, &priv_set);
You can use the PRIV_ASSERT macro or the str_to_priv_set(3TSOL) routine to assert privileges in a privilege set structure. str_to_priv_set() works well when you have two or more privileges to assert because you can do it in one statement; whereas, PRIV_ASSERT must be called for each privilege asserted in the set. This code uses the str_to_priv_set() routine for the allowed set and PRIV_ASSERT for the forced set. The str_to_priv() routine returns NULL on success and the string passed to it in priv_names on failure.
if((string = str_to_priv_set(priv_names, &priv_set, ",")) != NULL) printf("string = %s errno = %d\n", string, errno); PRIV_EMPTY(&priv_set); PRIV_ASSERT(&priv_set, PRIV_FILE_MAC_WRITE);
The next examples operate on the process sets. It might be helpful to see the of file and process privilege sets before any operations. The process sets are calculated from the algorithms in "Process Privilege Sets".
executable Allowed = file_mac_write,file_setpriv,proc_setid executable Forced = file_setpriv Permitted = file_mac_write,file_setpriv,proc_setid Effective = file_mac_write,file_setpriv,proc_setid Saved = file_mac_write,proc_setid Inheritable = file_mac_write&file_setpriv,proc_setid |
Privilege bracketing involves turning the effective privileges off (they are on and equal the permitted set by default), then turning on (making effective) only those permitted privileges needed for a given interface call, and turning them off when the privileged call completes.
A privileged process cannot be exploited by making privileges available to another process.
A bug in the application code is less likely to cause misuse of a privilege if the privilege is turned off when not needed.
The principle of least privilege is enforced because the process uses only the privileges it needs for the interfaces it is currently calling.
The evaluation of a trusted application is easier because privilege bracketing shows the person evaluating the code exactly where privileges are used.
When you analyze which privileges are needed for an interface, look at what the interface does and the purpose of the privileges described on the man page for that interface. Some privileges have broader effects than others and should be treated with greater scrutiny.
Privileges with broad effects are those that override mandatory access control or discretionary access control policies.
Privileges with narrower effects are those that allow access to a restricted operation such as mounting a file system.
For example, it is relatively easy to examine a segment of code to see that it uses a privilege with the mount(1M) system call and tell whether the use of that privilege can be exploited in any way. It is more difficult to tell if the use of a privilege to override the mandatory or discretionary access policy to access a restricted file can be exploited.
It is up to you to perform privilege bracketing in your code and to do it correctly. Always remember that all privileges override some policy that is not allowed to untrusted processes, and handle your use of privileges with the needed care.
The procedure for bracketing the setfpriv(1) system call and the effects it has on the effective set are summarized here. The code is shown in the next headings.
At the start of execution before bracketing, the permitted and effective sets contain these privileges:
Permitted = file_mac_write,file_setpriv,proc_setid Effective = file_mac_write,file_setpriv,proc_setid |
Clear the effective set at the beginning of the application.
Permitted = file_mac_write,file_setpriv,proc_setid Effective = none |
Bracket the setfpriv() system call.
Turn the file_setpriv privilege on in the effective set right before you call the setfpriv() system call.
Permitted = file_mac_write,file_setpriv,proc_setid Effective = file_setpriv |
Turn off the effective set immediately after the setfpriv() system call.
Permitted = file_mac_write,file_setpriv,proc_setid Effective = none |
The example uses set_effective_priv(3TSOL) to clear the effective set at the beginning of the application. The PRIV_SET parameter clears the effective privilege set, and the zero (0) indicates there is no parameter list of privilege IDs.
if (set_effective_priv(PRIV_SET, 0) == -1) perror("Cannot clear effective privileges");
Turning the entire effective privilege set off is followed by application code until a privilege is needed.
The example uses set_effective_priv(3TSOL) to bracket. The first call turns the file_setpriv privilege on (asserts it) in the effective set; the second call turns it off. The 1 indicates the privilege parameter list has one privilege constant (PRIV_FILE_SETPRIV) in it.
/* Turn file_setpriv on in effective set */ if (set_effective_priv(PRIV_ON, 1, PRIV_FILE_SETPRIV) == -1) perror("Cannot assert PRIV_FILE_SETPRIV"); /* Make interface call */ retval = setfpriv(execfile, PRIV_SET, PRIV_ALLOWED, &priv_get); /* Turn the file_setpriv privilege off */ if (set_effective_priv(PRIV_OFF, 1, PRIV_FILE_SETPRIV) == -1) perror("Cannot clear PRIV_FILE_SETPRIV"); /* Continue application code ...*/
This next example shows the body of the example application code with comments indicating the places where setfpriv(1) should be bracketed.
PRIV_EMPTY(&priv_get); PRIV_EMPTY(&priv_set); /* Turn file_setpriv on in the effective set */ retval = setfpriv(execfile, PRIV_SET, PRIV_ALLOWED, &priv_get); /* Turn the file_setpriv privilege off */ if((string = str_to_priv_set(priv_names, &priv_set, ",")) != NULL) printf("string = %s errno = %d\n", string, errno); /* Turn file_setpriv on in the effective set */ retval = setfpriv(execfile,PRIV_ON, PRIV_ALLOWED, &priv_set); /* Turn the file_setpriv privilege off */ retval = getfpriv(execfile, PRIV_ALLOWED, &priv_get); priv_set_to_str(&priv_get, ',', buffer, &length); printf("execfile Allowed = %s\n", buffer); PRIV_EMPTY(&priv_set); PRIV_EMPTY(&priv_get); PRIV_ASSERT(&priv_set, PRIV_FILE_MAC_WRITE); /* Turn file_setpriv on in the effective set */ retval = setfpriv(execfile, PRIV_ON, PRIV_FORCED, &priv_set); /* Turn the file_setpriv privilege off */ retval = getfpriv(execfile, PRIV_FORCED, &priv_get); priv_set_to_str(&priv_get, `,', buffer, &length); printf("execfile Forced =%s\n", buffer);
Applications can notify users that privileges are missing, and establish the privilege sets for a program.
An application can check the permitted privilege set to be sure the application has all privileges it needs to function. This way, if an application is missing a privilege, it can issue an error message to that effect. Continuing without all the needed privileges typically produces error messages that are more difficult to interpret.
The following example gets the permitted set and checks for PRIV_FILE_MAC_WRITE, PRIV_PROC_SETID, and PRIV_FILE_SETPRIV. The PRIV_ISSUBSET macro provides another way (not shown) to check if one privilege set contains all the privileges in another privilege set from within your source code.
/* Initialize privilege set data structure */ PRIV_EMPTY(&permitted_privs); /* Test for privileges in permitted set. */ if (getppriv(PRIV_PERMITTED, &permitted_privs) == -1) perror("Cannot get list of permitted privileges\n"); if (!PRIV_ISASSERT(&permitted_privs, PRIV_FILE_MAC_WRITE)) fprintf(stderr, "Need: file_mac_write.\n"); if (!PRIV_ISASSERT(&permitted_privs, PRIV_PROC_SETID)) fprintf(stderr, "Need: proc_setid.\n"); if (!PRIV_ISASSERT(&permitted_privs, PRIV_FILE_SETPRIV)) fprintf(stderr, "Need: file_setpriv.\n");
You can remove privileges from the permitted set, but once a privilege is removed it cannot be added back. Only privileges in the permitted set can be in the inheritable set so do not remove a permitted privilege that needs to be in the inheritable set. This example removes the file_mac_write privilege from the permitted set. The 1 indicates the parameter list has one privilege constant.
if(set_permitted_priv(PRIV_OFF, 1, PRIV_FILE_MAC_WRITE) == -1) perror ("Cannot remove file_mac_write from permitted set");
Before this call the permitted set contains these privileges:
executable Permitted = file_mac_write,file_setpriv,proc_setid |
After this call the permitted set contains these privileges:
executable Permitted = file_setpriv,proc_setid |
An application can check the saved privilege set to determine the origin of a privilege to take action based on the findings. This example gets the saved set and checks for PRIV_PROC_SETID and PRIV_FILE_SETPRIV and finds that the file_setpriv privilege is not inherited, but the proc_setid privilege is inherited.
PRIV_EMPTY(&saved_privs); if (getppriv(PRIV_SAVED, &saved_privs) == -1) perror("Cannot get list of saved privileges\n"); if (!PRIV_ISASSERT(&saved_privs, PRIV_PROC_SETID)) fprintf(stderr, "proc_setid not in saved set. \n"); if (!PRIV_ISASSERT(&saved_privs, PRIV_FILE_SETPRIV)) fprintf(stderr, "file_setpriv not in saved set.\n");
To set the privileges that will be active after a new program is started using exec(2), first clear the inheritable set of the process, then initialize it with the privileges that you want the program to inherit.
This example clears the inheritable privilege set. The PRIV_SET parameter clears the inheritable privilege set, and the zero (0) parameter indicates there is no parameter list of privilege IDs.
if (set_inheritable_priv(PRIV_SET, 0) == -1) perror("Cannot clear inheritable privileges");
Before this call the inheritable set contains these privileges:
Inheritable = file_mac_write,file_setpriv,proc_setid |
After this call the inheritable set contains this privilege:
Inheritable = none |
The following example sets the proc_setid privilege in the inheritable privilege set. Any privilege in the permitted set can be placed in the inheritable set and placing any other privilege in the inheritable set results in an Invalid Argument error. Because the proc_setid privilege is in the permitted set for executable, it can be placed in the inheritable set. Because it is also in the allowed set for execfile, it can be used by the new program when execfile is exec'd in "Execute a File".
if (set_inheritable_priv(PRIV_ON, 1, PRIV_PROC_SETID) == -1) perror("Cannot set proc_setid privilege in inheritable set");
After this call the inheritable set contains this privilege:
Inheritable = proc_setid |
When a child process is created by fork, its process sets are identical to the parent's process sets. This can be proven by querying the process privilege sets, forking a process, and querying the child process privilege sets:
Before the fork, the parent process has the following privileges:
Forked Inheritable = proc_setid Forked Saved = file_setpriv,proc_setid Forked Permitted = file_setpriv,proc_setid Forked Effective = none |
pid = fork(); if (pid > 0) exit(0); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_INHERITABLE, &priv_get); printf("retval = %d errno = %d\n", retval, errno); priv_set_to_str(&priv_get, ',', buffer, &length); printf("Forked Inheritable = %s\n", buffer); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_SAVED, &priv_get); printf("retval = %d errno = %d\n", retval, errno); priv_set_to_str(&priv_get, ',', buffer, &length); printf("Forked Saved = %s\n", buffer); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_PERMITTED, &priv_get); printf("retval = %d errno = %d\n", retval, errno); priv_set_to_str(&priv_get, ',', buffer, &length); printf("Forked Permitted = %s\n", buffer); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_EFFECTIVE, &priv_get); printf("retval = %d errno = %d\n", retval, errno); priv_set_to_str(&priv_get, ',', buffer, &length); printf("Forked Effective = %s\n", buffer);
After the fork(2) system call, the printf statements print the following:
Forked Inheritable = proc_setid Forked Saved = file_setpriv,proc_setid Forked Permitted = file_setpriv,proc_setid Forked Effective = none |
When a file is exec'd, the process sets are computed based on the algorithms described in "Process Privilege Sets".
The execfile for the new program has the following file privilege sets, which were set by the exec'ing process's application code:
execfile Allowed = file_mac_write,proc_setid execfile Forced = file_mac_write
The exec'ing process has the following process sets:
Exec'd Inheritable = proc_setid Exec'd Saved = file_setpriv,proc_setid Exec'd Permitted = file_setpriv,proc_setid Exec'd Effective = none |
retval = execv(execfile, argv);
After the exec(2) system call, the process sets are as follows.
execfile Allowed = file_mac_write,proc_setid execfile Forced = file_mac_write Exec'd Inheritable = proc_setid Exec'd Saved = proc_setid Exec'd Permitted = file_mac_write,proc_setid Exec'd Effective = file_mac_write,proc_setid |
The exec'd program's effective privileges are on by default. Because the new program has the proc_setid privilege in its effective set, you can call setuid(2) to see how the effective and saved sets change when the User ID changes. See "Change in User ID" for the discussion.
retval = setuid(0); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_EFFECTIVE, &priv_get); priv_set_to_str(&priv_get, ',', buffer, &length); printf("Executable setuid effective = %s\n", buffer); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_SAVED, &priv_get); priv_set_to_str(&priv_get, ',', buffer, &length); printf("Executable setuid saved = %s\n", buffer);
The printf statements print the following:
Executable setuid effective = none Executable setuid saved = file_mac_write,proc_setid |