You probably know this. Like all people, I have gaps in my knowledge, and this was one of them. I fixed it. Here’s what I learned, so maybe someone out there can learn too…

When you first get into Linux (or Unix), file permissions probably seem confusing if you’ve ever used any other operating system (like Windows/DOS, for instance). Then you learn what the rwx’s mean and everything makes sense.

I’m almost ashamed to admit that, until very recently, I was unfamiliar with setting the sticky bit and the suid/sgid bits numerically, because for some reason, it never clicked. Of course, it’s really easy to remember once you look at it the right way.

To illustrate what I’m talking about, lets look at some remedial file permissions…

The very basic file permissions are read, write, and execute, and are represented visually when performing ‘ls -l’ by a series of flags, ‘r’, ‘w’, and ‘x’. The group of three is presented three times: once for the user who owns the file, once for the group who owns the file, and ones for other users (who don’t belong to the group that owns the file). It looks like this:

$ ls -l test

-rw-r — r — 1 msimmons enterprise^admins 7 2010–02–25 12:17 test

It probably looks familiar. The first way that most of us learn to modify these commands is with the flag representations of the permissions, using the ‘chmod’ command. Like this:

msimmons@newcastle:~/test$ ls -al test.sh

-rw-r — r — 1 msimmons enterprise^admins 0 2010–06–10 16:24 test.sh

msimmons@newcastle:~/test$ chmod +x test.sh

msimmons@newcastle:~/test$ ls -al test.sh

-rwxr-xr-x 1 msimmons enterprise^admins 0 2010–06–10 16:24 test.sh

As you can see, after the ‘chmod’ command, the execute bits are set on the file. After a while, you probably learn the numeric codes, too. Then you can do things like this:

msimmons@newcastle:~/test$ ls -al test.sh

-rwxr-xr-x 1 msimmons enterprise^admins 0 2010–06–10 16:24 test.sh

msimmons@newcastle:~/test$ chmod 644 test.sh

msimmons@newcastle:~/test$ ls -al test.sh

-rw-r — r — 1 msimmons enterprise^admins 0 2010–06–10 16:24 test.sh

In case you’re rusty (or never learned), the numeric codes are actually in octal, and go from 0–7. Each of the flags are assigned a value:

rwx

---

421

Remember that the set of flags appears three times, for user, group, and others:

rwx rwx rwx

--- --- ---

421 421 421

This makes it really easy to numerically set the flags. You just take the match the flag at the top with the value at the bottom, group them by user, group, and other, then add up each group. Here’s an example:

I have a script that I want to make read, write, and execute by me, read and execute for the group, but not readable at all by anyone outside of the group. In other words, I want permissions to look like this:

rwx r-x ---

Looking at the table above, we can match the flags with the values like this: rwx r-x — -

421 401 000 That should be pretty apparent, right? Now we just add up each of the groups. rwx r-x — -

421 401 000

7 5 0 In other words, the permissions that I want are 750. So I can use chmod and specify that: msimmons@newcastle:~/test$ chmod 750 test.sh

msimmons@newcastle:~/test$ ls -l test.sh

-rwxr-x — — 1 msimmons enterprise^admins 0 2010–06–10 16:24 test.sh

Terrific, that worked perfectly. I feel like I should include a warning now, about the “others” group. If you set that writable…in other words, if that last “w” bit is set, anyone on the entire computer can write to your file, empty its contents, and replace them with whatever they want. If it’s a script, they can make that script do whatever they want. You almost certainly do not want this. Good security begins from the ground up, and good security is that of the least permissions. If someone only needs to read something, there is no reason to set the ‘write’ bit for them.

Note that you can technically remove the ‘write’ bit from yourself. ‘rm’ then asks if you want to remove the write-protected file (unless, of course, you use -f, in which case the file is merrily blown away).

Right, so that’s basic numeric usage. Most of that was probably remedial, however now is the exciting part…or at least, exciting to me, because I recently learned it.

SUID, SGID, and Sticky Bits

You have probably heard of SUID…you probably haven’t heard of SGID, and you might have heard of Sticky Bits. Here’s brief explanation…

The Set User ID (SUID) bit on a binary program indicates that, when the program is run, it operates as the user who owns the file. If a binary is owned by root, for instance, and has the SUID bit enabled, then that program runs as root, no matter who launches it. The ‘ping’ command is a perfect example:

msimmons@newcastle:~/test$ ls -l `which ping`

-rwsr-xr-x 1 root root 30856 2007–12–10 12:33 /bin/ping

The reason that ‘ping’ is SUID is because it needs to create raw ICMP sockets (which, if you’re not a network programmer, just means that it creates them manually so that they can have very specific flags and contents, rather than relying on the ‘safer’ standard calls). Because the root user is the only one with permission to do this, ping needs to run with root privileges. Hence the SUID bit.

This is probably ringing alarm bells in your head, which is a good thing. You should be concerned, because a bug (or exploit) in a SUID program is essentially a giant gaping hole in the security of your system. If someone managed to find a way to exploit ‘ping’ before it dropped privileges, then computers everywhere would be vulnerable to attack. For this reason, you should not, willy nilly, assign binaries SUID. Only well tested, hardened programs should get that flag (and ping is one of those).

As a matter of conversation, you cannot effectively make interpreted scripts SUID (which is a good thing). I’m honestly not 100% sure what, in the system, blocks it. I believe it’s the kernel, but maybe someone could enlighten me in the comments. The end result is that, regardless of the SUID flag being set, the script runs as the current user.

When you are looking at a directory listing, a file with the SUID bit set has an ‘s’ or an ‘S’ in the column that normally holds an ‘x’. Whether the ‘s’ is upper or lower case depends on whether the execute bit has been set. If it’s set, then the ‘s’ is lowercase. If the execute bit is not set, then it’s upper case. Here’s the example:

msimmons@newcastle:~/test$ touch test

msimmons@newcastle:~/test$ chmod 4755 test

msimmons@newcastle:~/test$ ls -al test

-rwsr-xr-x 1 msimmons enterprise^admins 0 2010–06–10 17:12 test

msimmons@newcastle:~/test$ chmod 4644 test

msimmons@newcastle:~/test$ ls -al test

-rwSr — r — 1 msimmons enterprise^admins 0 2010–06–10 17:12 test

Notice that when the execute bits are set (755), the ‘s’ is lowercase, but when the execute is not set (644), the flag turns to ‘S’.

The Set Group ID (SGID) bit works in a similar way, except that the group permissions it runs with belong to the group that owns it. I don’t find myself using it in that capacity much, but I do have use for the special effects that it has on directories.

The effect that SGID has on directories is that every file and directory created under that directory automatically inherit the group owner. An example is probably the easiest way:

Lets start by touching a file and examining the default owners:

msimmons@newcastle:~/test$ touch myfile

msimmons@newcastle:~/test$ ls -al myfile

-rw-r — r — 1 msimmons enterprise^admins 0 2010–06–10 17:17 myfile As you can see, the group owning it is my default group, enterprise^admins. Whenever I create files, that’s the group they get assigned to. Now we’ll make a directory, change ownership of it, add the SGID bit, and take a look:

msimmons@newcastle:~/test$ mkdir mydir

msimmons@newcastle:~/test$ chgrp domain^users mydir

msimmons@newcastle:~/test$ chmod 2755 mydir

msimmons@newcastle:~/test$ ls -ald mydir

drwxr-sr-x 2 msimmons domain^users 4096 2010–06–10 17:18 mydir As you can see, the SGID bit is set, and the group ownership is ‘domain^users’. Now, lets go into the directory, make a file, and see what happens. msimmons@newcastle:~/test$ cd mydir

msimmons@newcastle:~/test/mydir$ touch mynewfile

msimmons@newcastle:~/test/mydir$ ls -al mynewfile

-rw-r — r — 1 msimmons domain^users 0 2010–06–10 17:19 mynewfile There we have it. The file that we created in a directory with the SGID bit inherited the group ownership. But something else happens, too. Lets make a directory and have a look:

msimmons@newcastle:~/test/mydir$ mkdir mysubdir

msimmons@newcastle:~/test/mydir$ ls -ald mysubdir

drwxr-sr-x 2 msimmons domain^users 4096 2010–06–10 17:25 mysubdir Not only did the group ownership change to match, but the SGID bit is set as well. This enables us to set the bit on the parent directory and have the settings affect every directory that’s created. This is ideal for a shared working environment where everyone needs to create files that everyone else can access.

“Sticky Bit” is an interesting term. It can be set on files, but if it is, it’s ignored by Linux. There are other OSes that do use it, though. Instead, it’s very useful on directories.

Normally, files can be removed (or in strict Unix speak, ‘unlinked’) by anyone who has write access, even if they’re not the owner. This normally isn’t a problem. After all, if you can write to it, that means you should be able to erase it. There are times when this isn’t ideal, though, and that’s when the sticky bit comes to the rescue. When a directory has the sticky bit set, only the user who owns the file (or the root user) can remove it. Anyone else can still write to it, but removing it will get the error “rm: cannot remove `file’: Operation not permitted.

You can tell if a file or directory has the sticky bit set if the very last ‘x’ in the ‘others’ column is set to a ‘t’ or a ‘T’. Just like the SUID and SGID flags, you can tell if the underlying bit (in this case, the execute bit) is set by whether the ‘t’ is lower or upper case. If it’s lower case, the execute bit is set. If it’s uppercase, the bit is not set. Example:

msimmons@newcastle:~/test$ touch 1

msimmons@newcastle:~/test$ touch 2

msimmons@newcastle:~/test$ chmod 1001 1

msimmons@newcastle:~/test$ chmod 1000 2

msimmons@newcastle:~/test$ ls -al

total 24

drwxr-xr-x 2 msimmons enterprise^admins 4096 2010–06–10 17:47 .

drwxr-xr-x 171 msimmons enterprise^admins 20480 2010–06–10 16:53 ..

— — — — -t 1 msimmons enterprise^admins 0 2010–06–10 17:47 1

— — — — -T 1 msimmons enterprise^admins 0 2010–06–10 17:47 2

SUID, SGID, and Sticky Bit Numerics

Interspersed throughout the examples, you probably saw me using 4 digit numeric codes (such as 2755 when we were setting the SGID bit). If you leave off the first of the four bits, ‘chmod’ assumes the first bit is 0, and doesn’t set it (technically, any bits it doesn’t find, it assumes are 0, thus you could make a file

------r--

with a ‘chmod 4’ command). However, when you add that first number, you are instructing chmod to set the SUID, SGID, and Sticky Bit bits.

The breakdown occurs like this:

0 is nothing

1 is sticky bit

2 is SGID

3 is SGID and sticky bit

4 is SUID

5 is SUID and sticky bit

6 is SUID and SGID

7 is SUID, SGID, and sticky bit

Jeez, how could you not have that memorized?

Well, I couldn’t, anyway. And it didn’t click, until today. Those flags are octal, too, but the non-numeric flags are spread out over all three sets of rwx blocks. Maybe I can make it more clear with this snippet:

msimmons@newcastle:/tmp$ touch test

msimmons@newcastle:/tmp$ for PERM in `seq 0 7` ; do echo -n \

" %PERM " ; chmod ${PERM}000 test && ls -l test | awk '{print $1}' ; done

0 ----------

1 ---------T

2 ------S---

3 ------S--T

4 ---S------

5 ---S-----T

6 ---S--S---

7 ---S--S--T

A little bit of explanation is probably in order. First I touch a file. Then, I write a for loop that iterates from 0 to 7, and changes the permissions to 0000, then 1000, then 2000, then 3000, etc etc. It then prints out the permissions on the file at that time (along with the number that is set).

If you follow the pattern, it’s exactly like the ‘rwx’ pattern, with the same numbers. Instead of

rwx

---

421

we have

SST

---

421

The only difference is that the flags are set each in their own “user”, “group”, or “others” column.

Thus is becomes easy to numerically assign SUID, SGID, and Sticky Bit…

If you want a ‘normally’ permissioned directory (rwxr-xr-x), except you want the SGID and Sticky Bit set, you do it like this:

According to the previous table, the SGID is assigned 2, and the Sticky bit is assigned 1. 2+1=3, and the “normal” permissions come out to 755, so our command will be msimmons@newcastle:~/test$ mkdir dirname

msimmons@newcastle:~/test$ chmod 3755 dirname

msimmons@newcastle:~/test$ ls -ald dirname

drwxr-sr-t 2 msimmons enterprise^admins 4096 2010–06–10 17:55 dirname

There you have it. An easy way to remember the numeric codes for SUID, SGID, and Sticky Bit.

Please comment if you have any questions, if I didn’t explain clearly enough, or if I screwed something up. Thanks!