mode 2 seccompの話

この記事はカーネル/VM Advent Calendar 2013の記事です.

カーネル/VM Advent Calendar 2013 - Qiita [キータ]

前書き

にゃん↑ぱすー(挨拶).

さて,今年はmode 2 seccompの話です.
今年のセキュリティキャンプ2013,セキュアなシステムを作ろう組システムソフトウェアゼミでも取り扱いました.この辺りの話は,ゼミの卒業生第一号であるきゃにーさんのブログを見ると雰囲気が伝わるかなと思います.

seccamp2013卒業しました - 名前はまだ無い


彼女はC言語あんまり触ったことなく,準備期間でC言語の修行をおこない,1ヶ月と少しでプロセス分割とサンドボックス化まで出来るようになりました.頑張った!

 

さてseccomp の話題に戻します.巷ではseccomp 2, seccomp mode 2などとも書かれて,どうも表記に幅があります.

元々,システムコール番号をフィルターする seccompという仕組みがカーネルにあり,Linux Kernel 3.5からこれに追加する形でmode 2 seccompが導入されています.
この3.5から導入された仕組みのことを指す場合,mode 2 seccompあるいはseccomp filter, seccomp_bpfといった呼び方になるようです.

ざっくりとした説明などはこちらのスライドでどうぞ.


Linux Mode 2 Seccomp Tutorial // Speaker Deck

Mode 2 seccomp Internals

はてなブログソースコードの張り方とかがよく分からず面倒くさくなったので,ソースコードの解説などは今度まとめてqiitaとかで書こうと思います.めんど.

 

seccompのコードはlinuxソースコードを落としてきて,kernel/seccomp.cに実体がああります.メインとなるデータ構造はseccomp_filter構造体ですが本体はsock_filterという構造体のラッパー&リストになっています.は?(威圧)

 

sock_filterというのは,Berkeley Packet Filter(bpf)で使う,BPF instruction setをぶち込むためのデータ構造です.(半ギレ)

 

そう,Mode 2 seccompはBerkeley Packet Filterをバックエンドに使うのです.なんだろう,このリンゴ切るのにチェンソー使う感じ.どうしてこんなことになったのか?経緯はググると出てくるのですが,ざっと挙げるとこんな感じです.

 

1. レンダリングプロセスをサンドボックス化したいのん

Google Chromiumは設計段階からセキュリティを大きく重視しています.具体的には,javascriptやhtmlをレンダリングする処理をプロセス分割し,named pipeを使ってコードを送り,レンダリングプロセスでそれを実行,結果をshared memoryに描画するという感じになってます.これは第一にレンダリングに関わるjavascript実行エンジンとブラウザを切り離し,レンダリングプロセスが暴走したり,サーバからきたコードやレンダリングエンジンがバグっていてもブラウザ自体は動き続けるようにするためです.ただ,このレンダリングエンジンの脆弱性や悪意のあるjavascriptなどがブラウザを乗っ取るようなことを出来なくするため,レンダリングプロセスが出来る操作を大幅に制限しよう!という感じで設計していったと考えられます.

 

2. セキュリティアーキテクチャはOS(ディストリ)ごとに大きく変わるのでメンテナンスがめんどいのん

Google Chromiumサンドボックス化,PIDベースだと足りないけどSELinuxとか使うのは面倒過ぎて死ぬ 参照:Capsicum[USENIX Security '10]*1の論文

 

SELinuxを使う場合,ポリシーを書いてOSに強制してもらうわけですが,ディストリごとにタイプの割り当てが違っていたり,そもそもSELinuxがまともに動いてなかったり,ユーザーがOFFにしてしまうようなことになると意味ないわけです.

また,Chromeがリリースされた当初はWindows版がなかなかでなかったわけですが,この原因もこのサンドボックス化対応じゃないかなーと思っています.結局リリースされたWindows版も,FAT32ベースのシステムだとサンドボックスが上手く動かない,とか,その筋の人にしか分からない微妙な制限があったのが思い出されます.ま,Windowsで真面目にサンドボックス化しようと思っても当時のXPとかセキュリティはガバガバだからね,仕方ないね.

 

3.  ケーパビリティでは粒度が荒いのん

アクセス制御や権限の”粒度”というのは業界用語かもしれないのですが・・・ま,アクセス制御をおこなう単位のことです.*nixなシステムでは,プロセスごとにOSが特権を持たせ,プロセス生成時に親プロセスの特権を引き継ぐとか,特権の一部を剥奪することができます.ケーパビリティの例としては,CAP_SETUIDとか.CAP_SETUIDという特権を持っているプロセスは自身のUIDを変更できたりします.

権限の操作はprctlというシステムコールでおこなえます.プロセスがfork()するときに,子プロセスに対して,権限を指定する,要はプロセスが自発的に権限を放棄するわけですね.ここ重要です.

このようなケーパビリティベースの権限管理でも手の届かないところはあります.例えば,fork()を禁止したりとか.なので,じゃあシステムコール単位で権限を設定できるようにしようぜ!というところでしょうか.

 

4. mode 1 seccompは雑すぎなのん

とまぁここまで来て,Linux カーネルに元々あったsecure computing mode(seccompのオリジナル)が出てきます.secure computing modeは ケーパビリティの手の届いていないread/writeなど代表的なシステムコールを発行する権利を自ら放棄する仕組みです.ですが,mode 1 seccompは・・・大雑把過ぎたのです.read、write、sigreturn、exit をそれぞれ許可することしかできない.つらい.

 

とまぁこんないきさつがあって,システムコール単位の権限管理ができるものが欲しかったchromiumチームがmode 2 seccompを提案するわけです.しかし,どういうわけか彼らはBerkeley Packet Filterをバックエンドに持ってくるという発想に至ったようです.システムコールを制限するだけなら,プロセスごとにハッシュテーブルでシステムコール番号(たかだか数百)とその権限をもたせればいいんじゃないのかなーと思うのですが・・・

 

一応もっともらしい理由もあるようです.LWNの記事*2によると,

セキュリティサブシステムは性能もあんま考えないし,メンテもおろそかになりがちだし,カーネル内に似た機能があれば性能もよく考えられていて,メンテもよくされているわけだし,積極的に使えば良いじゃん.だから俺らはBerkeley Packet Filter使うわ.

みたいなことが書いてあります.うーん.2004年頃だったか,SELinuxが性能遅いというので海外産がロック周りの性能改善をやっていたという話も聞いたような.確かに一理あるという感じです.

 

まーでも,このbpfをバックエンドに使うことで別の問題が生じたりしています.Linux kernelの3.5だったか,bpf_jitというオプションが利用できるようになり,フィルタ性能を向上させるためにbpf処理をJITする機能がマージされました.カーネルでJITさせるというのは保守的なLinuxでよく入れたなぁと思うのですが,それだけ性能に対する要求が大きいというところでしょうか.ところが,このJITを使った攻撃手法もすぐに出現しました.

main is usually a function: Attacking hardened Linux systems with kernel JIT spraying

セキュリティサブシステムのバックエンドがセキュアじゃなかった,というなんだか悲しい出来事ですが,セキュリティサブシステムが独自の実装をしていればセキュアという訳でもないですからねぇ.

 

とまぁ,こんな感じでMode 2 seccompについてだらだらと書いてみました.

 

*1:いちいちフルペーパーなんか読めるかよって人は僕の論文輪講資料を参照

*2:Yet another new approach to seccomp [LWN.net]