Unixパーミッションについて調べ直してみる

chown, chgrp, chmodコマンドで扱われる、いわゆるUnixパーミッションについて、理解を深めるために整理してみた。

ユーザとグループ

Unix系システムでは、各ユーザは必ず一つのprimary groupに属する。 さらに、各ユーザはprimary groupのほかに、0個以上のsupplementary groupに属することができる。 これらはそれぞれuseraddあるいはusermodコマンドの-g, -Gオプションで指定される。

ユーザ、グループにはそれぞれID番号が割り振られており、これらは uid (user id), gid (group id) と表される。

ファイル・ディレクトリの所有ユーザ、所有グループ

Unix系システムでは、各ファイルはinode (index node)と呼ばれる構造体によって管理されている。 inodeはファイル本体(データブロック)を指すポインタのほか、ls -lで表示するさまざまな属性の情報を保持しており、その中にはファイルを所有するユーザを表すuid、ファイルを所有するグループを表すgid、ファイルに対するパーミッション情報 (mode) も含まれる。

また、ディレクトリもinodeによって管理され、その実体はディレクトリ下にあるファイルに対応するinodeのリスト (dentry) となっている。

パーミッション情報 (mode)

パーミッション情報は3つのユーザカテゴリのそれぞれに対する3種類のファイルパーミッションの有無と、3種類の特殊モードの有効・無効によって表される。

ユーザカテゴリ

ユーザカテゴリには、ファイルの所有ユーザ (owner)、所有グループに属するユーザ (group)、それら以外のユーザ (other) の3つがある。 これらは先に述べたものが優先される。 つまりファイルの所有ユーザかつ所有グループに属するユーザの場合、所有ユーザとして扱われ、他のカテゴリのパーミッションには影響されない。

$ id
uid=1001(john) gid=1001(john) groups=1001(john),27(sudo)
$ chmod 244 a.txt
$ ls -l
total 4
--w-r--r-- 1 john john 4 Sep  1 20:54 a.txt
$ cat a.txt
cat: a.txt: Permission denied

ファイルパーミッション

ファイルパーミッションには、read (r), write (w), execute (x) の3つがある。 対象がファイルの場合、これらは下記を意味する。

  • read: ファイルを読める
  • write: ファイルを書き換えられる
  • execute: ファイルをプログラムとして実行できる

一方、対象がディレクトリの場合は下記のようになる。

  • read: ディレクトリの中にあるファイルの一覧が見れる
  • write: ディレクトリの中でファイルの新規作成、削除ができる(既存ファイルの書き換えには影響しない)
  • execute: ディレクトリの中のファイル (inode) にアクセスできる(searchとも呼ばれる)

executeについては、ファイルとディレクトリでその意味が異なることに注意が必要である。

ディレクトリの場合、readおよびwriteはディレクトリ直下のファイルにしか影響しないが、executeはサブディレクトリ以下も含むファイル全体に再帰的に影響する。 つまり、ディレクトリを辿っていく途中で一つでもexecute権限のないディレクトリがある場合、そのディレクトリから先へは一切アクセスすることができない。 inode情報にアクセスすることができないため、ファイルの新規作成、削除、読み取り、書き換え、実行すべてが不可であり、たとえ目的とするファイルのパーミッションが777であってもこれは同様である。 ただし、カレントディレクトリがすでにexecute権限のないディレクトリのサブディレクトリ以下である場合においては、execute権限がないディレクトリを経由しない範囲で相対参照により自由にアクセスすることができる。

たとえば、あるディレクトリのパーミッションを700とした場合、そのディレクトリ以下のすべてのファイルは、設定したディレクトリの所有ユーザ以外からは再帰的にアクセス不可となる。

$ id
uid=1001(john) gid=1001(john) groups=1001(john),27(sudo)
$ ls -l b/
total 4
-rwxrwxrwx 1 john john 4 Sep  1 21:59 c.txt
$ sudo chown nobody:nogroup b
$ sudo chmod 700 b
$ ls -l
total 4
drwx------ 2 nobody nogroup 4096 Sep  1 21:59 b
$ ls -l b/
ls: cannot open directory b/: Permission denied
$ cat b/c.txt
cat: b/c.txt: Permission denied

特殊モード

特殊モードには、suid, sgid, sticky の3つがある。 対象がファイルの場合、これらは下記を意味する。

  • set-user-id (suid) bit: プログラム実行時の実効ユーザ (effective user) をファイルの所有ユーザとする
  • set-group-id (sgid) bit: プログラム実行時の実効グループ (effective group) をファイルの所有グループとする
  • sticky bit: (ほとんどのシステムでは影響しない)

一方、対象がディレクトリの場合は下記のようになる。

  • set-user-id (suid) bit: (ほとんどのシステムでは影響しない)
  • set-group-id (sgid) bit: 作成したファイル・ディレクトリの所有グループが指定したディレクトリの所有グループと同じになり、ディレクトリには自動的にsgid bitが継承される
  • sticky bit: ファイルの所有ユーザまたは指定したディレクトリの所有ユーザ以外について、ファイルの削除・リネームを禁止する

suidはpasswdやsudoなど一般ユーザが実行するプログラムが実行中にroot特権を必要とする場合に、所有ユーザをrootとした上で用いられる。 suidがセットされているコマンドは下記のようにして調べられる。

$ find /bin -perm -u+s
/bin/mount
/bin/umount
/bin/su
/bin/fusermount
/bin/ping6
/bin/ping

$ find /usr/bin -perm -u+s
/usr/bin/gpasswd
/usr/bin/sudo
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/passwd
/usr/bin/sudoedit
/usr/bin/newgrp
/usr/bin/traceroute6.iputils
/usr/bin/mtr
/usr/bin/at

ディレクトリにおけるsgid bitは、あるディレクトリ以下のすべてのファイルの所有グループを固定したい場合に用いることができる。 具体的には、wheel(あるいはsudo)などのsupplementary groupに所属するユーザ群が、個々のprimary groupを変えることなくディレクトリ以下を共有したい場合が挙げられる。

ディレクトリにおけるsticky bitは、不特定のユーザにファイルの新規作成を許したいが、自分が所有ユーザとなっているファイル以外の削除・リネームは許したくない場合に、write権限と合わせて用いられる。 一般にsticky bitは、/tmpディレクトリに対して指定される。 なお、削除・リネームが可能なのはファイルまたは指定したディレクトリの所有ユーザのみであり、ファイルまたはディレクトリの所有グループに属するユーザについては不可である。

$ id
uid=1001(john) gid=1001(john) groups=1001(john),27(sudo)
$ ls -l
total 4
drwxrwxrwt 2 nobody john 4096 Sep  1 23:02 d
$ ls -l d/
total 4
-rw-rw-r-- 1 nobody john 4 Sep  1 23:02 e.txt
$ rm d/e.txt
rm: cannot remove `d/e.txt': Operation not permitted

chmod -R a+rX

chmodコマンドでは、xの代わりにXという指定を行うことができる。 これは、ディレクトリについてはxと同様であるが、ファイルについてはowner, group, otherのいずれかですでにexecute権限が付与されている場合のみexecute権限を付与する。

この指定は、一般にはchmod -R a+rXの形で用いられる。 これを実行することで、すべてのユーザに対し、特定のディレクトリ以下で再帰的にread権限を付与した上で、ディレクトリまたは実行ファイルについてのみexecute権限も付与することができる。

あるいは、chmod -R a=rX,u+wとすることで、ディレクトリまたは実行ファイルについては755、一般ファイルについては644となるように再帰的にパーミッションを設定することができる。

$ ls -l
total 4
drwx------ 3 john john 4096 Sep  1 02:59 a
$ ls -l a/
total 12
drwx------ 2 john john 4096 Sep  1 02:59 b
-rw------- 1 john john    4 Sep  1 02:59 c.txt
-rwx------ 1 john john    4 Sep  1 02:59 d.sh
$ chmod -R a=rX,u+w a/
$ ls -l
total 4
drwxr-xr-x 3 john john 4096 Sep  1 02:59 a
$ ls -l a/
total 12
drwxr-xr-x 2 john john 4096 Sep  1 02:59 b
-rw-r--r-- 1 john john    4 Sep  1 02:59 c.txt
-rwxr-xr-x 1 john john    4 Sep  1 02:59 d.sh