Memra

SUID, SGID, the sticky bit, and reading a mode cold

◈ 13 cards

The three special bits explained from the contradictions they resolve, and how to read any twelve-bit mode — including the s/S and t/T cases — off a listing.

A contradiction the nine bits cannot resolve

Start with the problem, not the mechanism. Two requirements, both non-negotiable:

  1. Every user must be able to change their own password.
  2. No user may be able to write the password file, because writing it means editing everyone else’s entry as well as their own.

Nine permission bits cannot express that. Whatever you grant to run passwd is granted to everything else the user runs, and the file is either writable by them or it is not.

Set-user-ID (SUID) is the resolution. A program is normally executed with the effective user ID of whoever invoked it. With the SUID bit set, it is executed with the effective UID of the file’s owner instead. /usr/bin/passwd is owned by root and is SUID, so while it runs — and only while it runs — it holds root’s identity and can edit the password file. The privilege is attached to a single, carefully written program rather than to a user. The book names lp, mail, mkdir, mv and ps as other commands that have historically needed it; a non-system example is a game that must update a shared high-score table that players must not be able to edit by hand.

Set-group-ID (SGID) is the same mechanism at the group position: the process takes the group identity of the file’s group owner. Octal 2, or chmod g+s.

The sticky bit, and the /tmp contradiction

Another pair of requirements that the nine bits cannot express together:

  1. /tmp must be writable by everyone, so any program can create scratch files.
  2. No user may delete another user’s scratch files.

But deletion is governed by the directory, and a directory anyone can write is a directory in which anyone can delete anything. The sticky bit (octal 1, or chmod +t) changes that rule for one directory: with it set, a user may only remove or rename entries they own. That is why /tmp is drwxrwxrwt.

The sticky bit also has a historical meaning on an executable: a hint to keep the program’s text segment resident in memory or in swap after it exits, so the next run starts faster. That use is obsolete from 4.4BSD onward, because paging algorithms already keep hot pages resident — and the platforms now disagree about it. PC-BSD refuses chmod +t on a non-directory (Inappropriate file type or format), Solaris still permits it, and Linux stores the bit but the kernel ignores it on a regular file. Name the platform when you answer this one.

Twelve bits, not nine

The full mode is twelve bits, and a four-digit octal mode is <special><owner><group><other>. The leading digit is built the same way the others are — from the weights 4 = SUID, 2 = SGID, 1 = sticky — so chmod 6754 f sets SUID and SGID with mode 754, and chmod 7755 f sets all three at once.

Where the special bits appear, and what the case means

There is no fourth triple in ls -l. Each special bit is displayed in the execute column of the triple it belongs to, which means that column has to encode two bits at once. It does that with case:

  • lowercase — the special bit is set and the underlying execute bit is set;
  • UPPERCASE — the special bit is set and the underlying execute bit is clear.

So s at the owner position is SUID with owner-execute on; S is SUID with owner-execute off. Same for s/S at the group position, and t/T at the other position for sticky.

Worked example — set, observe, clear the x bit, observe again

$ chmod 4755 pwtool
$ ls -l pwtool
-rwsr-xr-x  1 ada  dev  8192 Sep  1 14:02 pwtool
$ chmod u-x pwtool
$ ls -l pwtool
-rwSr-xr-x  1 ada  dev  8192 Sep  1 14:02 pwtool

One bit changed between those two listings, and it was not the SUID bit — that stayed set the whole time. The case flipped because owner-execute went away. (A SUID program with its execute bit cleared is a useless object, which is exactly why the capital letter is worth flagging: it usually means a mistake.)

Now read one cold. -rwSr-sr-t, character by character: - is a regular file; owner rw then S, so SUID is set and owner-execute is clear; group r- then s, so SGID is set and group-execute is set; other r- then t, so sticky is set and other-execute is set. Special digit = 4 + 2 + 1 = 7, owner = 6, group = 5, other = 5. The mode is 7655.

11109876543210U4G2T1rwxrwxrwxleading octal digit = 4 SUID + 2 SGID + 1 sticky
Four octal digits, three bits each. The special digit is built from the same weights as the others, which is why 7 in front means all three special bits.
in the listingspecial bitthe x bitunderneathexamples at the owner xSUIDset-rwsr-xr-x 4755S at the owner xSUIDclear-rwSr--r-- 4644s at the group xSGIDset-rwxr-sr-x 2755S at the group xSGIDclear-rw-r-Sr-- 2644t at the other xstickysetdrwxrwxrwt 1777T at the other xstickycleardrwxrwxr-T 1774lowercase = both bits set; UPPERCASE = special set, execute clear.
The special bit has no column of its own, so the execute column carries two bits. Case is the second bit, and an uppercase letter usually means somebody made a mistake.
NORMAL ~/memra/learn/comp-325/suid-sgid-sticky-and-reading-a-mode-cold utf-8 LF