History
Originally, Unix only had permissions for the owning user, and for other users: there were no groups. See the documentation of Unix version 1, in particular chmod(1)
. So backward compatibility, if nothing else, requires permissions for the owning user.
Groups came later. ACLs allowing involving more than one group in the permissions of a file came much later.
Expressive power
Having three permissions for a file allows finer-grained permissions than having just two, at a very low cost (a lot lower than ACLs). For example, a file can have mode rw-r-----
: writable only by the owning user, readable by a group.
Another use case is setuid executables that are only executable by one group. For example, a program with mode rwsr-x---
owned by root:admin
allows only users in the admin
group to run that program as root.
“There are permissions that this scheme cannot express” is a terrible argument against it. The applicable criterion is, are there enough common expressible cases that justify the cost? In this instance, the cost is minimal, especially given the other reasons for the user/group/other triptych.
Simplicity
Having one group per user has a small but not insignificant management overhead. It is good that the extremely common case of a private file does not depend on this. An application that creates a private file (e.g. an email delivery program) knows that all it needs to do is give the file the mode 600. It doesn't need to traverse the group database looking for the group that only contains the user — and what to do if there is no such group or more than one?
Coming from another direction, suppose you see a file and you want to audit its permissions (i.e. check that they are what they should be). It's a lot easier when you can go “only accessible to the user, fine, next” than when you need to trace through group definitions. (Such complexity is the bane of systems that make heavy use of advanced features such as ACLs or capabilities.)
Orthogonality
Each process performs filesystem accesses as a particular user and a particular group (with more complicated rules on modern unices, which support supplementary groups). The user is used for a lot of things, including testing for root (uid 0) and signal delivery permission (user-based). There is a natural symmetry between distinguishing users and groups in process permissions and distinguishing users and groups in filesystem permissions.
From the standard:
In words, Common Access Determination Algorithm is to be interpreted as
follows (this description is a loose paraphrase of the common access
determination algorithm which is specified in detailed pseudocode in
ACL Managers ):
Match (in the sense defined in the pseudocode in ACL Managers )
the incoming PAC against the ACL's access ACLEs (in the
top-to-bottom order shown, namely: UO, U, FU, GO/G/FG, O, FO, AO),
stopping at the first such match (except that all matches are
considered "simultaneously" in the case of the indicated group-like
ACLEs), and note the permissions granted by the matched ACLE (or,
in the case of the group-like ACLEs, the union of the permissions
granted by all the matched ACLEs).
Mask (that is, intersect) the acquired permissions against the
permissions in the ACL's mask ACLEs, as necessary (namely, mask
with MASK_OBJ permissions if the match occurred in the center
column1, and/or mask with UNAUTHENTICATED permissions if the PAC
is unauthenticated). (If the ACL Manager doesn't support these two
mask ACLEs, this step is a null operation.)
(emphasis in original, footnote added)
That is, if there is a file with user and group root
and permissions 0600
called acl-test
, containing the single line read possible
, then:
$ getfacl acl-test
# file acl-test
# owner: root
# group: root
user::rw-
group::---
other::---
Now if I (as user fox
) attempt to cat
this:
$ cat acl-test
cat: acl-test: Permission denied
Group permissions are unioned
I happen to be in the groups users
and wheel
, so we can add specific ACLs for these groups:
# setfacl -m g:users:--- -m g:wheel:r-- acl-test
$ cat acl-test
read possible
This is because the group
entries (considered simultaneously) allow read permission to one of my groups. These can be combined:
# setfacl -m g:users:-w- acl-test
$ getfacl acl-test
# file: acl-test
# owner: root
# group: root
user::rw-
group::---
group:wheel:r--
group:users:-w-
mask::rw-
other::---
$ printf '%s\n' 'write possible' >> acl-test
$ cat acl-test
read possible
write possible
So now I can both read and write the file, even though the groups that allow these permissions are not the same group.
User-specific permissions override all groups
Since user rules apply before group rules, we can still restrict a given user from reading and/or writing contents:
# setfacl -m u:fox:--- acl-test
$ getfacl acl-test
# file: acl-test
# owner: root
# group: root
user::rw-
user:fox:---
group::---
group:wheel:r--
group:users:-w-
mask::rw-
other::---
$ cat acl-test
cat: acl-test: Permission denied
A mask, if set, overrides almost everything
If a file is meant to be truly read-only to anyone but the owner:
# setfacl -x u:fox -m g::rw- -m m:r-- acl-test
$ getfacl acl-test
# file: acl-test
# owner: root
# group: root
user::rw-
group::rw- #effective:r--
group:wheel:r--
group:users:-w- #effective:---
mask::r--
other::---
$ printf '%s\n' 'now writing is impossible' >> acl-test
bash: acl-test: Permission denied
# printf '%s\n' 'owner can still write' >> acl-test
Amusingly, the mask does not override the others permissions, so:
# setfacl -x g:users -x g:wheel -m o:rw- -n acl-test
$ getfacl acl-test
# file: acl-test
# owner: root
# group: root
user::rw-
group::rw- #effective:r--
mask::r--
other::rw-
$ printf '%s\n' 'others can write now' >> acl-test
# chown :users acl-test
$ printf '%s\n' 'but not members of the owning group' >> acl-test
bash: acl-test: Permission denied
1 The "center column" refers to this image and contains everything except UO and O, so the owning user and others are unaffected by a mask. All groups and non-owning users with defined rules are affected.
Best Answer
If it was the case then “other” permissions would apply to everyone.
That's different from your previous sentence. Here you're implying that the permissions are or'ed together, e.g. that userX has the read permission if userX owns the file and the file is user-readable, or if a group that userX belongs to owns the file and the file is group-readable, or if the file is other-readable. But that's not how it works. In fact,
o=rwx
means that therwx
permissions apply to others, but it doesn't say anything about entities that are not others.First, it doesn't directly matter which groups a user belongs to. The kernel doesn't have a notion of users belonging to groups. What the kernel maintains is, for every process, a user ID (the effective UID) and a list of group IDs (the effective GID and the supplementary GIDs). The groups are determined at login time, by the login process — it's the login process that reads the group database (e.g.
/etc/group
). User and group IDs are inherited by child processes¹.When a process tries to open a file, with traditional Unix permissions:
Only one set of rwx bits are ever used. User takes precedence over group which takes precedence over other. When there are access control lists, the algorithm described above is generalized:
See also Precedence of ACLS when a user belongs to multiple groups for more details about how ACL entries are used, including the effect of the mask.
Thus
-rw----r-- alice interns
indicates a file which can be read and written by Alice, and which can be read by all other users except interns. A file with permissions and ownership----rwx--- alice interns
is accessible only to interns except Alice (whether she is an intern or not). Since Alice can callchmod
to change the permissions, this does not provide any security; it's an edge case. On systems with ACLs, the generalized mechanism allows removing permissions from specific users or specific groups, which is sometimes useful.Using a single set of bits, rather than or-ing all the bits for each action (read, write, execute), has several advantages:
¹ They can change when a setuid or setgid process is executed. This isn't related to the issue at hand.