FreeBSD の Capsicum について
2021 年 1 月 21 日
(株)創夢 内藤 祐一郎
Capsicum とは
●
FreeBSD 10.0 (2014/01/20) からサポートされている
セキュリティの仕組み
●
設定不要、かつ、デフォルトで有効
●
きちんと実装されたアプリケーションならば、
使い勝手は変わらない
●
既に 7 年間使われ続けた実績がある
Capsicum とは
●
ユーザランドのプロセスが自発的に不要な権限を落とすこ
とでセキュリティを高めようというものです
●
万が一、プロセスを乗っ取られても権限がないため情報漏
洩などを防ぐことができます
●
必要最小限の権限を capability と言ったりします
●
Linux の capability とは異なりますので
本発表では Linux の capability のことは忘れてください
実装コマンド例
●
dd
●
md5 / sha
●
diff / diff3
●
basename
●
iconv
●
head / tail
●
printenv
●
logger
●
ping / ping6
●
traceroute / 6
●
tcpdump
●
rtsold
●
sshd
●
hastd
●
iscsid
●
dhclient
●
bhyve
●
readelf
●
nm
●
strings
●
xz
●
hexdump
●
kdump
●
elfdump
など・・・
実装プログラム数
分類 プログラム数
bin 4
sbin 8
usr.bin 37
usr.sbin 9
contrib 12
crypto 1
合計 71 FreeBSD Current r36584
(2020/09/18) での数
ソースコード単位で集計
Capsicum の設計
●
既存の UNIX の仕組みを壊さずに拡張する
●
ファイルディスクリプタなどをそのまま使う
●
新しいモードとして capability mode を追加する
●
このモードは抜けることができない&子プロセスにも引き
継がれる
●
capability mode ではグローバルな名前空間へのアクセス
を禁止する
グローバルな名前空間
名前空間 概要
Process ID プロセスに与えられる識別子
File paths ルートから始まる階層構造を持つファイルへのアクセス方法
NFS file handles NFS サーバとクライアントで使われるファイルの識別子
File system ID マウントポイント毎に割り当てられる ID
Protocol addresses IPv4 / IPv6 アドレスやホスト / ドメイン名など
Sysctl MIB sysctl の識別子 (kern.ostype など )
System V IPC System V の共有メモリ、メッセージなど
POSIX IPC メッセージキュー、セマフォなど
Jails FreeBSD のプロセスを閉じ込める仕組み
CPU sets 使える CPU の一覧
ローカルな名前空間
●
ファイルディスクリプタ
●
仮想メモリアドレス
●
スレッド ID
●
シグナルマスク
●
プロセスタイマー
●
プロセス優先度
●
リソース制限
グローバルな名前空間へのアクセス例
●
open(2) / stat(2) / chdir(2) / unlink(2) / execve(2)
– 引数にファイルパスが入るので NG
●
fork(2) / wait(2) / wait4(2)
– 戻り値や引数に PID が入るので NG
●
connect(2) / bind(2)
– 引数にネットワークアドレスが入るので NG
グローバルな名前空間へのアクセス例
●
kill(2)
– 自分自身へのシグナルなら OK それ以外は NG
●
sysctl(3)
– 自身の情報に関するものならば OK それ以外は NG
●
shm_open(2)
– 匿名 (ANONYMOUS) ならば OK それ以外は NG
ローカルな名前空間へのアクセス例
●
openat(2) / fstat(2) / fchdir(2) / unlinkat(2) / fexecve(2)
– ファイルディスクリプタを基にしたアクセスのため OK
●
pdfork(2)
– pid の代わりにプロセスディスクリプタを返す
●
connectat(2) / bindat(2)
– ファイルディスクリプタを基にしたアクセスのため OK
– ただし UNIX Domain Socket しか使えない
ローカルな名前空間へのアクセス例
●
read(2) / write(2) / close(2) / pipe(2) / dup(2) / select(2)
– ファイルディスクリプタの操作のため OK
●
getpid(2) / getuid(2)
– 自身の情報を返すだけのため OK
●
sigaction(2)
– 自分のシグナルに関する設定のため OK
Capsicum の実装
●
capability mode に入るシステムコール cap_enter(2) を
新設します
●
capability mode で呼び出せないシステムコールは
エラーを返します
●
capability mode で呼び出せるシステムコールのうち
条件のあるものは条件を追記します
●
capability mode で呼び出しても OK なものは
何も変更しません
Capsicum 実装のメリット
●
全てシステムコールの入り口で判定することができます
●
ファイルシステムやプロトコルスタックなどのサブシステ
ムに手を入れる必要がありません
●
sysctl MIB については全てに capability mode でのアクセ
ス権限フラグを新設しました
アプリケーションからの使い方
1.アクセスするリソースは事前に取得します
– open(2) などでファイルを開いておきます
2. cap_enter(2) を呼び出します
– capability mode に入ります
3.実際の処理を行います
– ここで予期せぬ入力からバッファオーバーランなどを
引き起こしても、事前に取得したリソース以外へアクセ
スできないため、情報流出などを防ぐことができます
さらなる制限
●
既に取得したファイルディスクリプタに更なる制限を加え
ることができます
●
例えば、あるファイルディスクリプタに対して
READ | FCNTL | IOCTL | SEEK | EVENT
を許可することで、読み込み、 fcntl(2) 、 ioctl(2) 、シー
ク、待ち合わせを許可して他を制限することができます
●
主に呼び出せるシステムコールの種類毎に権限が定義され
ており、 rights(4) に 78 種類あります
さらなる制限
●
また、 ioctl(2) については発行するリクエスト番号を制限
することができます
●
使用するリクエスト番号を予め 256 個まで登録することが
できます
●
登録されたリクエスト番号以外をエラーとします
casper ライブラリ
●
capability mode の中でいくつかの制限を緩和するための
ライブラリ
●
casper と呼ばれる代理プロセスを経由して、制限された
機能を呼び出します。
●
実装は libcasper.so の中にあります。
●
casper は種類毎にサービスとして分けられており、
FreeBSD Current r366713 時点で全6種類あります
( FreeBSD 12.1 Release では 7 種類)
casper ライブラリの実装
メインプロセス
(capability mode)
casper サービス
(non capability mode)
cap_init()
- socketpair()
- fork()
サービスチャンネル
メインチャンネル
メインチャンネル close
cap_service_open()
cap_enter()
casper ライブラリの実装
●
casper はサービスとして存在していますが、実体は自分自
身が fork(2) した子プロセスです
●
capability mode に入る前に初期化 (cap_init(3)) し、自分自
身を fork(2) して、子プロセスを立ち上げます
●
子プロセスとの間は socketpair(2) で UNIX Domain
Socket のペアで繋ぎます
●
cap_init(3) の戻り値としてこの通信路を含んだ構造体を返
します
casper ライブラリの実装
●
子プロセスの casper に必要なサービスを要求
(cap_service_open(3)) すると該当サービスへの通信路と
して同様の UNIX Domain Socket が開かれます
●
サービス毎の通信路を通してリモート関数呼び出しが行わ
れます
●
各サービスの実体も同じ子プロセスです
●
従って代理実行されている処理は直列に動作します
●
サービスを取得したら casper への通信路を閉じます
cap_pwd
●
/etc/passwd, NIS,LDAP などのユーザ情報を扱うサービスです
●
getpwent(3), getpwnam(3) などの関数呼び出しを代理で実行して
くれます
●
予め必要な関数名を定義しておいて、その呼び出しだけを代理実
行させることができます
●
必要なフィールドを定義しておいて、そのフィールドだけを返す
ようにすることもできます
●
検索できるユーザ名または UID を制限することもできます
cap_grp
●
/etc/group, NIS, LDAP などグループに関する情報を扱うサービ
スです
●
getgwent(3), getgrnam(3) などの呼び出しを代理で実行してく
れます
●
予め必要な関数名を定義しておいて、その呼び出しだけを代理
実行させることができます
●
必要なフィールドを定義しておいて、そのフィールドだけを返
すようにすることもできます
●
検索できるグループ名または GID を制限することもできます
cap_sysctl
●
sysctl を扱うためのサービスです
●
sysctl(3), sysctlbyname(3) などの呼び出しを代理で実行し
てくれます
●
予め操作する MIB を定義しておいて、その MIB だけを操
作することができます
cap_syslog
●
syslog を扱うためのサービスです
●
syslog(3), openlog(3), closelog(3) などの呼び出しを代理
で実行してくれます
●
syslog 出力のみのため、特に追加的な制限はありません
cap_net
●
ネットワークを扱うためのサービスです
●
bind(2), connect(2), getaddrinfo(3) などの呼び出しを代理
で実行してくれます
●
予め呼び出す関数の種類を制限することができます
●
名前解決するホスト名を制限することができます
●
逆引きする IP アドレスを制限することができます
●
connect(2) するアドレスを制限することができます
●
bind(2) するアドレスを制限することができます
cap_fileargs
●
コマンドライン引数で指定されたファイル名を開くための
サービスです
●
既存のコマンドを capability mode で動かすようにすると
きにコードの変更を少なくするために作られました
●
argv の配列を渡すとそこに書かれたファイル名であれば
open(2) を代理実行してくれます
●
open(2) したファイルディスクリプタに追加制限を加える
ことができます
統廃合されたサービス
●
cap_dns は cap_net に統合されました
●
cap_random は getrandom(2) が実装されたため不要にな
りました
capability mode でのプログラミング
●
基本的には全てのリソースを予め取得しておきます
●
そうでなければ必要なディレクトリを全て open(2) してお
くと、 openat(2) で開くことができます
●
ネットワークアクセスには cap_net を使用して、ホワイト
リスト内の相手とだけ通信します
●
自分で casper サービスを追加するのも手です
●
逆に守りたい部分だけ fork(2) 後に子プロセスで処理し、
結果を親プロセスに返すという手もあります
capability mode でのプログラミング
●
子プロセスの起動は現時点ではスタティックリンクされた
バイナリのみ起動できます
●
ダイナミックリンクされたバイナリは起動時にダイナミッ
クリンカ (/libexec/ld-elf.so.1) が必要ですがこれにアクセス
する権限がありません
●
子プロセスは起動直後から capability mode のためほとん
ど何もできません
●
fdfork(2), fexec(2), poll(2), close(2) を呼び出すことで子プ
ロセスの実行から待ち合わせ、終了処理が行えます
capability mode でのデバッグ
●
システムコールが capability 違反を検出すると
ECAPMODE の errno が返ります
●
sysctl kern.trap_enotcap=1
に設定すると ECAPMODE が返るときにプロセスがコアダ
ンプするようになります
●
コアファイルからスタックトレースなどを探るとどの処理
で落ちているのかが分かりやすくなります
Capsicum の限界
●
shell が書けない
– shell は全ての資源にアクセスできるべきです
●
スクリプト言語と相性が悪い
– リソース管理がカプセル化されているため
– 事前に全てのリソースを把握するのが困難
●
ライブラリが使いにくい
– ファイルディスクリプタを受け渡すインタフェースに
変える必要があります
実例( rtsold )
●
2020 年 12 月に rtsold の SA がでました
– FreeBSD-SA-20:32.rtsold
●
root で任意のコードが実行できる可能性があるものです
●
影響は既に bind しているネットワークインタフェースを
操作できる程度に制限されていた
➔ capsicum により情報漏洩のリスクは避けられた
実例( tcpdump )
●
2017 年 tcpdump 4.9.0 に複数の脆弱性
CVE-2017-5486
CVE-2017-5485
CVE-2017-5484
CVE-2017-5483
CVE-2017-5482
CVE-2017-5342
CVE-2017-5341
CVE-2017-5205
CVE-2017-5204
CVE-2017-5203
CVE-2017-5202
・・・
いずれも任意のコードが
実行されうる重大なもの
実例( tcpdump )
●
2017 年当時既に FreeBSD の tcpdump は capability mode
で動作していました
●
任意のコードが実行されたとしてもできることはほとんど
ありませんでした
●
FreeBSD Project から Security Advisory は出ませんした
●
tcpdump は次のリリース (11.2 RELEASE 2018/6/28) で
バージョンアップされました
おわり
●
質問などがありましたらどうぞ

FreeBSD Capsicum