Linuxのプロセス(実行中のプログラム)にはいろいろなIDが関わってくる。
・プロセスID(PID)
そのプロセスに付けされた番号、正の値。違うプロセスは違うプロセスIDを持つ。プログラム中から自分のプロセスIDを取得するにはgetpid()を使う。
・親プロセスID(PPID)
そのプロセスを起動したプロセスのプロセスID。getppid()で取得できる。
・実ユーザID(UID)
そのプロセスがどのユーザによって所有されているかを表す。プロセス開始時は親プロセスのUIDが引き継がれる。
・実効ユーザID(EUID)
これがよくわからないので、少し調べて見る。まず以下のCソース(showid.c)をコンパイルして実行して見る。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main ( )
{
printf ( "getpid() : %d\n" , getpid ( ) ) ;
printf ( "getuid() : %d\n" , getuid ( ) ) ;
printf ( "geteuid() : %d\n" , geteuid ( ) ) ;
return 0 ;
}
実効結果は以下
getpid() : 5005
getuid() : 506
geteuid() : 506
よってデフォルトでは実効ユーザIDはUIDと同じになる。
ところでLinuxにはset-user-IDという概念がある。passwdコマンドは/etc/passwdファイルを変更するが、このファイルはrootしか変更できない。しかしこのコマンドは一般ユーザが実行してもこのファイルを変更できる。passwdコマンドのファイル属性を見て見ると以下のようになっている。
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 25700 2008-04-08 22:48 /usr/bin/passwd
$
そこでさっき作ったshowidの属性をこれと同じにして実行して見る。
$ gcc showid.c -o showid
$ su
パスワード:
# chown root:root showid
# chmod 755 showid
# chmod u+s showid
# ls -l
合計 12
-rwsr-xr-x 1 root root 5217 2009-01-23 09:52 showid
-rw-rw-r-- 1 ebisu ebisu 206 2009-01-23 09:34 showid.c
# exit
exit
$ ./showid
getpid() : 5240
getuid() : 506
geteuid() : 0
$
geteuidが0になった。これはこのファイルの所有者rootのUIDだろう。ところでEUIDが0になることはわかったがこれはどこで使われているのか?man credentialsを見て見ると、EUIDはメッセージキュー、共有メモリ、セマフォなどにアクセスする際にアクセス許可の判定に用いられるものらしい。しかしながらファイルのアクセス判定にはファイルシステムIDが用いられるようなので、passwdの例は適切ではないのかもしれない。ファイルシステムIDについてはあとで考えよう。
・保存set-user-id(saved set-user-id)
マニュアルには「set-user-idされたプログラムにおいて、プログラムの実行時に設定された実効ユーザIDのコピーを保存する」と書いてある。上の例で見たとおりset-user-idされたプログラムは実効ユーザIDがrootなので、0が保存されていることになる。さらにマニュアルには「set-user-ID プログラムは、実効ユーザ ID を実ユーザID と保存 set-user-ID の間で行ったり来たり切り替えることで、特権を得たり落としたりできる」とあるので、0であった実効ユーザIDを一般ユーザに戻したり、また0にすることができるということだろう。そして、現在の保存set-user-idはgetresuid()で取得できるようなので、showid.cを書き換えておこう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void show ( )
{
uid_t ruid , euid , suid ;
if ( 0 != getresuid ( & ruid , & euid , & suid ) )
{
perror ( "getresuid :" ) ;
exit ( 1 ) ;
}
printf ( "getpid() : %d\n" , getpid ( ) ) ;
printf ( "uid : %d\n" , ruid ) ;
printf ( "euid() : %d\n" , euid ) ;
printf ( "suid() : %d\n" , suid ) ;
}
int main ( )
{
show ( ) ;
return 0 ;
}
これをコンパイルして普通に実行した場合は以下のようになる。
getpid() : 5410
uid : 506
euid() : 506
suid() : 506
set-user-IDして実行すると以下のようになる。
getpid() : 5453
uid : 506
euid() : 0
suid() : 0
とすればあと問題になるのはどーやって実効ユーザIDを切り替えるか、ということだが、これにはseteuid()を使えばいいようだ。そこでshowid.cを書き換えてみる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void show ( )
{
uid_t ruid , euid , suid ;
if ( 0 != getresuid ( & ruid , & euid , & suid ) )
{
perror ( "getresuid :" ) ;
exit ( 1 ) ;
}
printf ( "getpid() : %d\n" , getpid ( ) ) ;
printf ( "uid : %d\n" , ruid ) ;
printf ( "euid() : %d\n" , euid ) ;
printf ( "suid() : %d\n" , suid ) ;
}
int main ( )
{
uid_t ruid , euid , suid ;
printf ( "-------- default ----------\n" ) ;
show ( ) ;
if ( seteuid ( getuid ( ) ) )
{
perror ( "seteuid:" ) ;
exit ( 2 ) ;
}
printf ( "-------- after seteuid(getuid())----------\n" ) ;
show ( ) ;
if ( 0 != getresuid ( & ruid , & euid , & suid ) )
{
perror ( "getresuid :" ) ;
exit ( 3 ) ;
}
if ( seteuid ( suid ) )
{
perror ( "seteuid:" ) ;
exit ( 4 ) ;
}
printf ( "-------- after seteuid(suid)----------\n" ) ;
show ( ) ;
return 0 ;
}
これを普通に実行すると以下のようになる。
-------- default ----------
getpid() : 5770
uid : 506
euid() : 506
suid() : 506
-------- after seteuid(getuid())----------
getpid() : 5770
uid : 506
euid() : 506
suid() : 506
-------- after seteuid(suid)----------
getpid() : 5770
uid : 506
euid() : 506
suid() : 506
set-user-IDして実行すると以下。
-------- default ----------
getpid() : 5772
uid : 506
euid() : 0
suid() : 0
-------- after seteuid(getuid())----------
getpid() : 5772
uid : 506
euid() : 506
suid() : 0
-------- after seteuid(suid)----------
getpid() : 5772
uid : 506
euid() : 0
suid() : 0
実効ユーザIDを切り替えることができた。しかしseteuid()以外にも、setresuid()がありこれは実ユーザも保存set-user-IDも切り替えることができるみたいだ。こうなってくるとわけがわからなくなるのでこのあたりにしておこう。
もうひとつ気になるのは保存set-user-IDなるものは何のためにあるのかがよくわからなかった。プログラム実行時は実効ユーザIDと保存set-user-IDは常に同じになるはずだから、わざわざカーネルレベルで保存set-user-IDを持っておく必要性がよくわからない。きっと理由はあるか、勘違いしているだけだろうから、ここではもう考えないでおこう。
・ファイルシステムID
マニュアルによるとファイルシステムIDは、実効ユーザIDが変更されるたびにそれと同じ値にするそうなので、上のpasswdの例はこれで納得がいった。もちろんファイルシステムIDだけを変更することはできるようだが、ここではもうオシマイにしたい。