前回からだいぶ間隔が空いてしまいました。前回の最後に案内したudzuraさんのCRIUに関する記事はもう少し時間がかかるようですので、
今回から数回は、
setuid
一般的にはUNIX系のOSでは、
一般ユーザは、ping
コマンドは特権が必要で、setuid
されており、
$ ls -l $(which ping)
-rwsr-xr-x 1 root root 64424 Jun 28 20:05 /bin/ping
↑(このsがsetuidされている印)
ケーパビリティ
先に紹介したping
コマンドは、setuid
されており、
逆に言うとRawソケットを使うのに必要な権限のみが必要なのに、ping
コマンドに渡していることになります。これはセキュリティ上の観点から望ましいことではありません。
Linuxにはこのような場合に使える、
例えば、CAP_
というケーパビリティがあればRawソケットを使用できます。Linuxカーネルでは、/usr/
で定義されています。
$ grep "#define CAP_" /usr/include/linux/capability.h #define CAP_CHOWN 0 #define CAP_DAC_OVERRIDE 1 #define CAP_DAC_READ_SEARCH 2 #define CAP_FOWNER 3 #define CAP_FSETID 4 #define CAP_KILL 5 :(略)
ケーパビリティの実装は結構古くて、capabilities(7)
に載っています。
プロセスのケーパビリティ
プロセス
- Permitted
- EffectiveとInheritableで持つことを許されるケーパビリティセット
- Inheritable
execve(2)
した際に継承できるケーパビリティセット- Effective
- 実際にカーネルがスレッドの実行権限を判定するのに使うケーパビリティセット
- Ambient
- 特権のない
(setuid/ setgidされていない) プログラムを execve(2)
した際に子プロセスに継承されるケーパビリティセット(Linux 4. 3以降で使用可能)
さらに、
実際にカーネルがプロセスが持つ権限をチェックする時は、
そして、
capset(2)
:システムコールでケーパビリティを設定するexecve(2)
:システムコールの前後でケーパビリティが変化するprctl(2)
:システムコールでAmbientケーパビリティやバウンディングセットを設定する
execve(2)
システムコールはプログラムを実行するためのシステムコールです。このシステムコールとケーパビリティの関係については次回で説明します。
実行中のプロセスが持つケーパビリティを確認する
では実際にプロセスでケーパビリティがどのように設定されているかを見てみましょう。
プロセス/proc/<PID>/status
ファイルで確認できます。例えばPIDが1であるinit
のPermittedCapPrm
)、CapEff
)、CapBnd
)
$ grep Cap /proc/1/status CapInh: 0000000000000000 CapPrm: 0000003fffffffff CapEff: 0000003fffffffff CapBnd: 0000003fffffffff CapAmb: 0000000000000000
特定のケーパビリティだけが設定されている、ntpd
は次のようになっています。
$ grep Cap /proc/$(pgrep ntpd)/status CapInh: 0000000000000000 CapPrm: 0000000002000400 CapEff: 0000000002000400 CapBnd: 0000003fffffffff CapAmb: 0000000000000000
上記の例のように status
ファイルでケーパビリティを確認できます。しかし、
もう少しわかりやすくプロセスのケーパビリティセットを確認したい場合はlibcap
に含まれるgetpcaps
コマンドが使えますlibcap2-bin
パッケージに含まれています)。
$ getpcaps $(pgrep ntpd) Capabilities for `21935': = cap_net_bind_service,cap_sys_time+ep
ポート番号1024番未満を使うには特権が必要です。しかし、ntpd
は一般ユーザであるntp
ユーザ権限で動作しており、ntpd
で使うポートである123番ポートは使えないはずです。しかし、cap_
を持っているので123番ポートを使えます。
また、cap_
を持っていますので、
つまりntpd
は必要最低限のケーパビリティのみを持った状態で実行されているということです。不要な特権を持たずにデーモンが実行されていますので、
プロセスのケーパビリティを操作する
実際にケーパビリティによって必要な特権のみを持って動く例を見ましたので、
特権を持っていないプロセスがいきなり特権を取得できませんので、
ケーパビリティを持っていなければ、libcap
に含まれるcapsh
コマンドで簡単に試せます。ここで指定するケーパビリティは、capability.
で定義されているケーパビリティを指定します
# id
uid=0(root) gid=0(root) groups=0(root)
# capsh --drop="cap_net_raw" -- -c "ping -c 1 127.0.0.1"
ping: socket: Operation not permitted
(cap_net_rawを削除して実行したのでrootユーザなのにpingが実行できない)
cap_
ケーパビリティを削除したときにケーパビリティがどのようになっているのかを確認してみましょう。まずはrootで普通に実行しているbash
が持つケーパビリティです。
# grep Cap /proc/$$/status CapInh: 0000000000000000 CapPrm: 0000003fffffffff CapEff: 0000003fffffffff CapBnd: 0000003fffffffff CapAmb: 0000000000000000
サポートされているケーパビリティすべてが有効になっているのがわかります。ここでcap_
を削除すると次のようになります。
# capsh --drop="cap_net_raw" --
(cap_net_rawを削除してbashを実行)
# grep Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000003fffffdfff
CapEff: 0000003fffffdfff
CapBnd: 0000003fffffdfff
CapAmb: 0000000000000000
/usr/
を見ると、CAP_
は13ですので、
ファイルケーパビリティ
プロセスは、
セキュリティ的には不要な特権が減っていくわけですから意味があるわけですが、ping
コマンドに設定されていたsetuid
のようなケースです。
そこでsetuid
のように、
ファイルケーパビリティも、
次の例は CentOS 8にインストールされているping
コマンドです。
$ ls -l /bin/ping -rwxr-xr-x. 1 root root 69160 May 11 23:22 /bin/ping (setuidされていない) $ id -u 1000 (一般ユーザで実行) $ ping -c 1 127.0.0.1 PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.021 ms --- 127.0.0.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.021/0.021/0.021/0.000 ms (pingが実行できた)
このようにsetuid
されていませんがping
コマンドは実行できています。これはファイルケーパビリティが設定されているからです。
ファイルケーパビリティを確認するにはlibcap
に含まれるgetcap
コマンドを使います。
$ getcap /bin/ping /bin/ping = cap_net_admin,cap_net_raw+p
これは、/bin/
にはファイルケーパビリティのPermittedケーパビリティとして、cap_
とcap_
ケーパビリティが設定されているという意味です
ファイルケーパビリティは安全のためにコピーすると設定が外れます
$ cp /bin/ping . $ getcap ./ping (ファイルケーパビリティが何も設定されていない) $ ./ping 127.0.0.1 ping: socket: Operation not permitted (権限がないので実行できない)
このようにファイルケーパビリティが設定されていないので、ping
コマンドは一般ユーザーでは実行できません。
ここでコピーしたping
コマンドを一般ユーザ権限で実行できるようにファイルケーパビリティでcap_
を付与してみましょう。同じくCentOS 8で実行しています。
$ sudo setcap cap_net_raw=p ./ping (permittedをオン) $ getcap ./ping ./ping = cap_net_raw+p (cap_net_rawが設定された) $ ./ping -c 1 127.0.0.1 PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.023 ms --- 127.0.0.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.023/0.023/0.023/0.000 ms (実行できた)
ファイルケーパビリティを設定するには特権が必要ですので、setcap
コマンドだけはroot権限で実行しています。
まとめ
今回は、
また、
実は今回の記事は、
その前提知識として、
次回まででケーパビリティ全体について説明を終わらせ、