3.1. プロセス

Unix ライクなシステムでは、ユーザレベルの動作はプロセスを動かすことで 実現しています。 Unix システムの大部分は「スレッド」をプロセスとは独立した概念として サポートしています。 スレッドはプロセス内でメモリを共有していて、システムのスケジューラは スレッド自身をスケジューリングしています。 Linux はこれとは異なる方法で実行しています(私は優れたやり方だと思います)。 スレッドとプロセスは基本的に違いはありません。 そのかわり Linux においては、プロセスがもう 1 つのプロセスを起こした時に、 どのリソースを共有するのかを選択できます(たとえばメモリを共有するとか)。 そして Linux カーネルは、スレッドレベルで速度が最適になるように動作します。 詳しい情報は clone(2) を見てください。 Linux のカーネル開発者は「スレッド」や「プロセス」というかわりに、「タスク」 という言葉をよく使う点に注意してください。しかし、対外的なドキュメントには、 プロセスという言葉を使います(なので、ここではプロセスという言葉を使います)。 マルチ・スレッドのアプリケーションをプログラムする場合には、上記のような 違いを隠蔽するため、通常は標準のスレッドのどれかを使った方が適切です。 こうすると移植性が高くなるだけでなく、ライブラリが間接的なレベルの機能追加 を提供できます。これは、複数のアプリケーションレベルのスレッドを、あたかも オペレーティングシステムの単独のスレッドとして実行することで実現しています。 こうすることで、あるシステムの何らかのアプリケーションは、性能がいくらか向上 できます。

3.1.1. プロセスの属性

ここでは、Unix ライクなシステムで動くプロセスそれぞれが持っている代表的 な属性を挙げておきます。

ここでは、プロセスに関連してはいるものの、あまり一般的ではないものを説明 します。

  • fsuid、fsgid ―― uid と gid はファイルシステムへのアクセス権限をチェックするのに 使います。 通常は、それぞれ euid や egid と同じです。 この属性は Linux 独自です。

  • ケイパビリティ―― POSIX で定義しているケイパビリティ情報。プロセスについての ケイパビリティは、3 つあります。それは、実効、継承、許可です。POSIX ケイパ ビリティについての詳しい情報は、下記を見てください。 Linux のカーネル 2.2 バージョン以上であればこの機能をサポートしています。 他の Unix ライクなシステムでもサポートしていますが、一般的というわけでは ありません。

Linux において、どの属性が各々のプロセスに関連しているのかを正確に知る必要 があるなら、Linux のソースコードが最も信頼のおける情報源です。特に、 /usr/include/linux/sched.h にある task_struct という定義は重要です。

新しいプロセスを起こすには、fork(2) システムコールを使うのが一般的です。 BSD は vfork(2) という最適化をはかった改良版を導入しました。 vfork(2) の基本的な考えは単純で、使う必要がなければ使わ ないです。 詳しくは、Section 7.6 を見てください。

Linux では、固有のシステムコールである clone(2)をサポートしています。 このシステムコールは fork(2) と同様に動作しますが、共有したいリソース (たとえば、メモリやファイル・ディスクリプタ等)を指定できます。 さまざまな BSD システムでは rfork() システムコール(オリジナルは Plan9 で開発)を実装しています。呼び出し方は異なりますが、基本的な考え方は 同じです(これも共有するものに対しての操作を強化して、プロセスを生成 します)。 プログラムに移植性を持たせるなら、できればこれらのシステムコールを そのまま使うべきではありません。上記でも述べましたが、移植性を持たせる なら、それらのシステムコールを用いたスレッドライブラリをもとにした方が良い でしょう。

このドキュメントは、プログラムを書くための完璧なチュートリアルではありません。 したがって、プロセスを扱った一般的に利用できる情報については省略しています。 さらに情報として wait(2) や exit(2) といったドキュメントも利用できます。

3.1.2. POSIX ケイパビリティ

POSIX ケイパビリティはビットで値を構成し、普通 root が持っている権限を 分割し、より特化した権限を持つ大きな組み合わせとして再構成します。 POSIX ケイパビリティは、IEEE 標準のドラフトで定義されています。したがって Linux 固有の機能でもありませんし、他の Unix ライクなシステムで広く採用 されてもいません。 Linux のカーネル 2.0 では POSIX ケイパビリティをサポートしていませんが、 2.2 ではプロセスについて、POSIX ケイパビリティをサポートしています。 Linux のドキュメント(このドキュメントも含め)の中で、「root の権限が必要で ある」と書いてあった場合、「ケイパビリティが必要である」とほぼ同じ意味に なる、とケイパビリティについてのドキュメントに述べられています。 個々のケイパビリティについて知りたい場合は、ケイパビリティに関する ドキュメントを読んでください。

Linux においては、ファイルシステム上にあるファイルに対してケイパビリティを 適用することが最終目的です。しかし、これを書いている時点では、まだ実装されて いません。転送機能に対するケイパビリティはサポートされていますが、デフォルト では無効になっています。Linux のカーネル 2.2.11 ではケイパビリティをより 身近に利用しやすくする機能が加わりました。その機能は「ケイパビリティ・ バウンディング・セット(capability bounding set)」です。 ケイパビリティ・バウンディング・セットは、ケイパビリティのリストの 1 つで、 システム上のどのプロセスもその管理下に入ります(さもなければ、特別な init プロセスだけが管理します)。 ケイパビリティがバウンディング・セットになければ、権限がどれであっても プロセスから利用できません。 この機能を使っている例として、カーネルモジュールの読み込みを無効にする 機能が挙げられます。 試験的ではありますが、この機能を駆使したツールに LCAP http://pweb.netcom.com/~spoon/lcap/ があります。 【訳註:LCAP は、カーネルがサポートしているケイパビリティを無効にすることに よって、システムをより安全にする仕組みです】

POSIX ケイパビリティについての詳しい情報は、 ftp://ftp.kernel.org/pub/linux/libs/security/linux-privs で利用できます。

3.1.3. プロセスの生成とその操作

プロセスは fork(2) やお薦めできない vfork(2)、Linux 独自の clone(2)を使って 生成します。これらのシステムコールはすべて、既存のプロセスを複製し、2 つの プロセスを生成します。 プロセスは execve(2)、もしくはそのフロントエンド(exec(3)や system(3)、popen(3) がその例)をコールして、別のプログラムを実行できます。

setuid や setgid してあるプログラムを実行すると、プロセスの euid や egid (それぞれ)には、そのファイルに設定してある値がセットされます。 この相関関係は、いにしえの Unix では競合状態を引き起こし、セキュリティ 上の弱点の源となっていました。以前は setuid や setgid してあるスクリプト に対応していたからです。 カーネルが、どのインタプリタが動作するのかを見るためにファイルをオープン する時と、(id がセットされ)インタプリタに制御を返ってインタプリタが ファイルを再オープンしてスクリプトを解釈する時の間に、攻撃者がファイルを 操ってしまう可能性がありました(直接にもしくはシンボリック・リンク経由で)。

Unix ライクなシステムは、setuid したスクリプトのセキュリティ上の問題に 対して、さまざまなやり方で対処してきました。 あるシステムでは、実行スクリプトに setuid や setgid のビットが立ってい ると、それを完全に無視しています。Linux もこれに該当し、安全さは疑いよう がありません。 ごく最近の System V R4 や BSD 4.4 のリリースでは、カーネルの競合状態を避ける ために、また違ったアプローチをとっています。 これらのシステムでは、カーネルは id がセットしてあるスクリプト名を インタプリタに渡す時に、パス名を渡さず(これが競合状態を起こすことに なります)、/dev/fd/3 というファイル名を渡します。これはスペシャル ファイルで、既にそのスクリプトでオープンしていますので、攻撃者が悪用する 競合状態は起こりえません。 このようなシステムにおいても、安全さが必要なプログラムに setuid や setgid してあるシェル・スクリプト言語を使うのには賛成できません。理由は下記で 論じます。

プロセスがさまざまな uid や gid の値に変化をもたらすケースも存在します。 setuid(2) や seteuid(2)、setreuid(2)、Linux 独自の setfsuid(2)を見て ください。 特に保存ユーザ id(suid)という属性はそのケースに該当し、本当に信頼された プログラムが、一時的に uid を変更してしまいます。 Unix ライクなシステムでは、suid を下記のルールの下でサポートしています。 ruid の変更もしくは euid が ruid と異なる値になった場合は、suid には 新しい euid の値が設定されます。 特権を持たないユーザは、自分の suid から自分の euid を、ruid から euid を、 euid から ruid を設定できます。

Linux 独自の fsuid プロセス属性は、NFS サーバのようなプログラムで、ファイル システムの権限に限って指定された uid に許可をあたえます。そのプロセスへ シグナルを送る許可は与えません。 euid が変更されると fsuid は新しい euid の値に変更されます。fsuid は setfsuid(2)という Linux 独自のシステムコールを使って設定できます。 root 以外から呼び出された場合は、fsuid には現在の ruid や euid、seuid、 あるいは現在の fsuid しか設定できません。