setuid and setgid
setuid is a flag that allows an application to temporarily change the permissions
of the user running the process by setting the effective user id to the program's
owner user id. setgid is a flag that allows an application to temporarily change
the permissions of the group running the process by setting the effective group
id to the program owner's group id.
When a binary executable file has been given the setuid attribute, normal users
on the system can execute this file and gain the privileges of the user who owns
the file (commonly root) within the created process. When root privileges have
been gained within the process, the application can then perform tasks on the
system that regular users normally would be restricted from doing. The invoking
user will be prohibited by the system from altering the new process in any way,
such as by using ptrace, LD_LIBRARY_PATH or sending signals to it (signals from
the terminal will still be accepted, however). Due to the increased likelihood
of security flaws, many operating systems ignore the setuid attribute when
applied to executable shell scripts.
The presence of setuid executables justifies the fact that the chroot system call
is not available to non-root users on Unix.
The setuid and setgid bits are normally set with the command chmod by setting the
high-order octal to 4 or 2 (or 6 to set both). `chmod 6711` will set the setuid
and setgid bit (6) make the file read/write/executable for the owner (7) and
executable by the group and others (11). All chmod flags are octal, and the least
significant bit of the high-order octal is used for a special mode known as the
sticky bit. Most implementations of the chmod command also support symbolic
arguments to set these bits. This is shown in the demonstration below as the
'chmod ug+s' command.
The setuid and setgid flags, when set on a directory, have an entirely different
meaning. Setting the setgid permission on a directory (chmod g+s) causes new
files and subdirectories created within it to inherit its group, rather than the
primary group of the user who created the file (the owner is never affected,
only the group). Newly created subdirectories inherit the setgid bit. Note that
setting the setgid permission on a directory only affects the group of new files
and subdirectories created after the setgid bit is set, and is not applied to
existing entities. Setting the setgid bit on existing subdirectories must be
done manually, with a command such as the following:
find /path/to/directory -type d -print0 | xargs -0 chmod g+s
or
[root@foo]# find /path/to/directory -type d -exec chmod g+s {} \;
The setuid permission set on a directory is ignored on UNIX and GNU/Linux
systems. FreeBSD can be configured to interpret it similarly to setgid, namely,
to force all files and sub-directories to be owned by the top directory owner.
To disable a setuid util:
chmod a-s /path/to/util
To disable a setgid util:
chmod a-g /path/to/util
--------------------------------------------------------------------------------
The demonstration C program below simply obtains and reveals the real and effective
user and group id currently assigned to the process. The commands shown first compile
the process as user `bob` and subsequently use `chmod` to establish the setuid ad
setgid bits. The `su` command, itself a client of the setuid feature, is then used
to assume the id of `alice`. The effectiveness of the `chmod` command is checked with
'ls -l', and finally the demonstration program is run, revealing the expected identity
change, consistent with the /etc/passwd file. Note that the demonstration program
listed below will silently fail to change the effective UID if run on a volume mounted
with the 'nosuid' option.
[bob@foo]$ cat /etc/passwd
alice:x:1007:1007::/home/alice:/bin/bash
bob:x:1008:1008::/home/bob:/bin/bash
[bob@foo]$ cat printid.c
#include
#include
#include
#include
int main(void) {
printf("Real UID\t= %d\n", getuid());
printf("Effective UID\t= %d\n", geteuid());
printf("Real GID\t= %d\n", getgid());
printf("Effective GID\t= %d\n", getegid());
return EXIT_SUCCESS;
}
[bob@foo]$ gcc -Wall printid.c -o printid
[bob@foo]$ chmod ug+s printid
[bob@foo]$ su alice
[alice@foo]$ ls -l
-rwsr-sr-x 1 bob bob 6944 2007-11-06 10:22 printid
[alice@foo]$ ./printid
Real UID = 1007
Effective UID = 1008
Real GID = 1007
Effective GID = 1008
--------------------------------------------------------------------------------
The Sticky Bit
The sticky bit was introduced in the Fifth Edition of Unix in 1974 for use with
pure executable files. When set, it instructed the operating system to retain
the text segment of the program in swap space after the process exited. This
speeded subsequent executions by allowing the kernel to make a single operation
of moving the program from swap to real memory. Thus, frequently-used programs
like editors would load notably faster. One notable problem with "stickied"
programs was replacing the executable (for instance, during patching); to do so
required removing the sticky bit from the executable, executing the program and
exiting to flush the cache, replacing the binary executable, and then restoring
the sticky bit.
Currently, this behavior is only operative in HP-UX. Solaris appears to have
abandoned this in 2005. The 4.4-Lite release of BSD retained the old sticky bit
behavior but it has been subsequently dropped from OpenBSD (as of release 3.7)
and FreeBSD (as of release 2.2.1); it remains in NetBSD. No version of Linux
has ever supported the traditional behavior.
The most common use of the sticky bit today is on directories, where, when set,
items inside the directory can be renamed or deleted only by the item's owner,
the directory's owner, or the superuser; without the sticky bit set, any user
with write and execute permissions for the directory can rename or delete
contained files, regardless of owner. Typically this is set on the /tmp
directory to prevent ordinary users from deleting or moving other users' files.
This feature was introduced in 4.3BSD in 1986 and today it is found in most
modern Unix systems.
The sticky bit can be set using the chmod command and can be set using its
octal mode 1000 or by its symbol t (s is already used by the setuid bit). For
example, to add the bit on the directory /usr/local/tmp, one would type
chmod +t /usr/local/tmp. Or, to make sure that directory has standard tmp
permissions, one could also type chmod 1777 /usr/local/tmp.
In Unix symbolic file system permission notation, the sticky bit is represented
by the letter t in the final character-place. For instance, on Solaris 8, the
/tmp directory, which by default has the sticky-bit set, shows up as:
$ ls -ld /tmp
drwxrwxrwt 4 root sys 485 Nov 10 06:01 /tmp
If the sticky-bit is set on a file or directory without the execution bit set
for the others category (non-user-owner and non-group-owner), it is indicated
with a capital T:
# ls -l test
-rw-r--r-- 1 root other 0 Nov 10 12:57 test
# chmod +t test; ls -l test
-rw-r--r-T 1 root other 0 Nov 10 12:57 test