● execve()のエラー
execve(2)系は、
- 帰ってこなかったら成功
- 帰ってきたら失敗
だけかと思いきや、帰ってこずにSIGKILLで殺され失敗というケースが存在する。binfmt_elf.cでmmap()するときに、物理メモリが足りなかった・仮想アドレスが足りなかった場合に起こりうる。
通常はなかなか踏まないが、
- arm
- CONFIG_DEFAULT_MMAP_MIN_ADDRを定義している
- ユーザプログラムの.textの開始位置が小さい
場合にハマる。(参考、https://stackoverflow.com/questions/49778460/linux-kills-process-when-calling-execv-execve) また、rootだと仮想アドレスの使用範囲制限を受けずに動いてしまう点もいやらしい。
execの不可解なエラーについては、ld.soの指定pathのミスもどうぞ。
● setsuid()時のcapabilities
setuid(2)を使いrootからroot以外に降格する場合、capabilities(7)はすべて失う。たとえcapありファイルを実行していたりcapsetp(3)されていても失う。
これを防ぐには、prctl(2)のPR_SET_KEEPCAPSを使う。
● capsh
上記の通り、setuid(2)でrootから降格するとcapabilities(7)をすべて失うので、降格しながらcapabilitiesを得るにはこんな感じのことをする。
- 1) fork()
- 2) setuid()
- 3) execve()
3)でcapabilitiesの立ったファイルを実行すれば、親はrootのままだし、子供は降格しつつcapabilitiesを得るし、となる。
ファイルのcapabilitiesについてはsetcap(8)あたり参考に。Filesystemのxattrを必要とするので事前に確認を。例えばsquashfsの場合CONFIG_SQUASHFS_XATTRが必要になる。
シェルスクリプトなどではこの1),2),3)の順に起動することができない。そんな場合のために、setuid()するためだけのwrapperプログラムを1)と2)の間に挟むやり方がある。そのため(だけではないが)に作られたのがcapsh(1)である。
● file sealing
File sealingという機能がある。prctl(2)のF_SEAL_SEAL, F_SEAL_SHRINK, F_SEAL_GROW, F_SEAL_WRITEあたり。ファイルの更新を一時的に凍結できる。何に使われるんだろ、flock(2)が紳士協定でしか役に立たないからそれの代わりに使える?
memfd_create(2)が必須でtmpfsでのみサポート、らしいが、試していないので詳しいことはわからない。
● capgetp()のサンプル
libcapのcapgetp(3)を使うサンプルプログラムが探しても全然見つからなくて試すのに苦労したので、下記に自作したのを貼っておく
execve(2)系は、
- 帰ってこなかったら成功
- 帰ってきたら失敗
だけかと思いきや、帰ってこずにSIGKILLで殺され失敗というケースが存在する。binfmt_elf.cでmmap()するときに、物理メモリが足りなかった・仮想アドレスが足りなかった場合に起こりうる。
通常はなかなか踏まないが、
- arm
- CONFIG_DEFAULT_MMAP_MIN_ADDRを定義している
- ユーザプログラムの.textの開始位置が小さい
場合にハマる。(参考、https://stackoverflow.com/questions/49778460/linux-kills-process-when-calling-execv-execve) また、rootだと仮想アドレスの使用範囲制限を受けずに動いてしまう点もいやらしい。
execの不可解なエラーについては、ld.soの指定pathのミスもどうぞ。
● setsuid()時のcapabilities
setuid(2)を使いrootからroot以外に降格する場合、capabilities(7)はすべて失う。たとえcapありファイルを実行していたりcapsetp(3)されていても失う。
これを防ぐには、prctl(2)のPR_SET_KEEPCAPSを使う。
● capsh
上記の通り、setuid(2)でrootから降格するとcapabilities(7)をすべて失うので、降格しながらcapabilitiesを得るにはこんな感じのことをする。
- 1) fork()
- 2) setuid()
- 3) execve()
3)でcapabilitiesの立ったファイルを実行すれば、親はrootのままだし、子供は降格しつつcapabilitiesを得るし、となる。
ファイルのcapabilitiesについてはsetcap(8)あたり参考に。Filesystemのxattrを必要とするので事前に確認を。例えばsquashfsの場合CONFIG_SQUASHFS_XATTRが必要になる。
シェルスクリプトなどではこの1),2),3)の順に起動することができない。そんな場合のために、setuid()するためだけのwrapperプログラムを1)と2)の間に挟むやり方がある。そのため(だけではないが)に作られたのがcapsh(1)である。
● file sealing
File sealingという機能がある。prctl(2)のF_SEAL_SEAL, F_SEAL_SHRINK, F_SEAL_GROW, F_SEAL_WRITEあたり。ファイルの更新を一時的に凍結できる。何に使われるんだろ、flock(2)が紳士協定でしか役に立たないからそれの代わりに使える?
memfd_create(2)が必須でtmpfsでのみサポート、らしいが、試していないので詳しいことはわからない。
● capgetp()のサンプル
libcapのcapgetp(3)を使うサンプルプログラムが探しても全然見つからなくて試すのに苦労したので、下記に自作したのを貼っておく
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <signal.h> #include <unistd.h> #include <err.h> #include <sys/capability.h> static void my_cap_show(pid_t pid){ cap_t cap_d = cap_init(); int ret = capgetp(pid, cap_d); if (ret) { perror("capgetp()"); } else { char *cap_text = cap_to_text(cap_d, NULL); printf("capgetp() = %s\n", cap_text); cap_free(cap_text); } ret = cap_free(cap_d); if (ret) { perror("cap_free"); } } int main (){ my_cap_show(0); return 0; }