6. LinuxのUIDのはなし
setuidとかseteuidとかの話。いまお行儀のよいdaemonを書こうとおもって、そこら辺を調べていた過程でUIDの話が出てきた。
実行中のプログラムにはReal UID, Effective UID, Saved UIDの3つがあって、普通は全部一緒になっている。使われるのはEffective UIDで、それ以外はUID操作に関する特殊用途で持っている。
Real UIDとEffective UIDのはなし
プログラムによっては(Effective) UIDを変更したいことがある。(Effective) UIDを変更したいシチュエーションは2つある
- 一時的に(Effective) UIDを変更して、権限を落とす。(後で戻したい)
- 恒久的に(Effective) UIDを変更して、権限を落とす。(後で戻せないようにしたい)
一時的なほうは別ユーザーとしてファイルを作りたいときに使って、恒久的なほうはrootで起動されたdaemonの権限を落としたいというときに使うらしい。
ここで問題なのは、一時的に変更するほうをやりたいときに、単純に一つしかUIDを持っていないと、権限の降格はできても、復帰がセキュリティ上難しくなるということだ。一つしかUIDを持っていないと、次のような2つのUID操作シーケンスが区別できない。
- root -> user -> root
- user -> root
1はrootから始まってuserに降格した後にrootに復帰している。逆に2はuserがrootに昇格しようとしている。1は許したいが(一時的に権限を落としただけ)、2は勝手にrootになられては困るので、禁止したい。
ここで、Real UIDとEffective UIDが出てくる。この2つのUIDがあると先の2つのシーケンスが区別できる。
1の場合、Effective UIDのみ変えればReal UIDはrootのままなので、もともとがrootであると判定できて、復帰できる。
| RUID | EUID |
(1) | root | root |
<- seteuid(user)
(2) | root | user |
<- seteuid(root)
(3) | root | root |
2の場合、Effective UIDをrootにしようとしてもどのUIDもuserなので、昇格は禁止され る。
| RUID | EUID |
(1) | user | user |
<- seteuid(root) EPERM
また、恒久的に落としたい場合はReal UIDも変更すればいい。
| RUID | EUID |
(1) | root | root |
<- setuid(user)
(2) | user | user |
<- seteuid(root) EPERM
Saved UIDのはなし
Linuxではファイルにsetuidというパーミッションが付けられる。これは何かというと、通常の実行可能ファイルは実行ユーザーのReal UIDとEffective UIDを持つようになっているけれども、このパーミッションが付いているファイルはEffective UIDがファイルオーナーの権限になる。例えばuserがrootがオーナーの普通のファイルとrootがオーナーのsetuidファイルを起動すると、起動時のUIDは次のようになる。
| RUID | EUID |
normal file | user | user |
setuid file | user | root |
こういう仕組みがあるとなんで便利なのかというと、例えばsudoみたいなプログラムが書けるようになる。rootじゃないといじれないようなリソースを使って動くプログラムで、それを一般ユーザーにも使えるようにさせたいという場合に便利ということらしい。setuidされたファイルはほとんどroot権限なので、以降、setuidファイルはrootオーナーのファイルという前提で話す。
setuidファイルはEffective UIDがrootになっているが、ここで先のような一時的にUIDを変えたり、恒久的にUIDを変えたりしたいとする。恒久的に変える方はそのまま同じようにsetuidすればいいのだが、一時的に変える方では、root -> user -> rootのようなシーケンスが、Real UIDとEffective UIDだけの世界では実現できない。
| RUID | EUID |
(1) | user | root |
<- (seteuid)
(2) | user | user |
<- (seteuid) EPERM
ここで2番めの操作はうまくいって欲しい。そこでSaved UIDが出てくる。
| RUID | EUID | SUID |
(1) | user | root | root |
<- (seteuid)
(2) | user | user | root |
<- (seteuid)
(3) | user | root | root |
このようにSaved UIDがあると、setuidファイルじゃないときと同じように権限の降格・復帰ができる。
Real UIDも起動時にファイルオーナーであってもいい気がしてくるが、そうするとsudoが誰のパスワードを聞けばいいのかわからなくなる。やっぱりReal UIDは実行を開始したユーザーである必要がある。