ここでは、スーパーユーザーモデルを使って特権を囲い込みする方法と、最小特権モデルを使って特権を囲い込みする方法を比較します。
次の例では、スーパーユーザーモデルで特権操作を囲い込みする方法を示します。
/* Program start */ uid = getuid(); seteuid(uid); /* Privilege bracketing */ seteuid(0); /* Code requiring superuser capability */ ... /* End of code requiring superuser capability */ seteuid(uid); ... /* Give up superuser ability permanently */ setreuid(uid,uid);
この例では、最小特権モデルで特権操作を囲い込みする方法を示します。この例では、次のように仮定します。
プログラムは setuid 0 である。
setuid 0 の結果として、許可されたセットと実効セットは最初、すべての特権に設定されている。
継承可能なセットは最初、基本特権に設定されている。
制限セットは最初、すべての特権に設定されている。
コードリストに続いて、この例の説明があります。
このソースコード例は、Sun ダウンロードセンターからダウンロードすることも可能です。http://www.sun.com/download/products.xml?id=41912db5 を参照してください。
1  #include <priv.h>
2  /* Always use the basic set. The Basic set might grow in future
3   * releases and potentially retrict actions that are currently
4   * unrestricted */
5  priv_set_t *temp = priv_str_to_set("basic", ",", NULL);
6  /* PRIV_FILE_DAC_READ is needed in this example */
7  (void) priv_addset(temp, PRIV_FILE_DAC_READ);
8  /* PRIV_PROC_EXEC is no longer needed after program starts */
9  (void) priv_delset(temp, PRIV_PROC_EXEC);
10 /* Compute the set of privileges that are never needed */
11  priv_inverse(temp);
12  /* Remove the set of unneeded privs from Permitted (and by
13   * implication from Effective) */
14  (void) setppriv(PRIV_OFF, PRIV_PERMITTED, temp);
15  /* Remove unneeded priv set from Limit to be safe */
16  (void) setppriv(PRIV_OFF, PRIV_LIMIT, temp);
17  /* Done with temp */
18  priv_freeset(temp);
19  /* Now get rid of the euid that brought us extra privs */
20  (void) seteuid(getuid());
21  /* Toggle PRIV_FILE_DAC_READ off while it is unneeded */
22  priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, NULL);
23  /* Toggle PRIV_FILE_DAC_READ on when special privilege is needed*/
24  priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, NULL);
25  fd = open("/some/retricted/file", O_RDONLY);
26  /* Toggle PRIV_FILE_DAC_READ off after it has been used */
27  priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, NULL);
28  /* Remove PRIV_FILE_DAC_READ when it is no longer needed */
29  priv_set(PRIV_OFF, PRIV_ALLSETS, PRIV_FILE_DAC_READ, NULL);
このプログラムでは、temp という名前の変数が定義されます。temp 変数は、このプログラムで不要な特権のセットを判定します。まず 5 行目で、temp が基本特権セットを含むように定義されています。7 行目で、file_dac_read 特権が temp に追加されています。proc_exec 特権は、新しいプロセスに対して exec(1) を実行するために必要ですが、このプログラムでは許可されていません。したがって、9 行目で、proc_exec が temp から削除されています。これで、exec(1) コマンドを使って新しいプロセスを実行できなくなります。
この時点で temp に含まれているのは、このプログラムが必要とする特権だけ、つまり基本セット + file_dac_read - proc_exec です。11 行目では、priv_inverse() 関数が、temp の反転を計算し、temp の値をその反転値にリセットしています。この反転は、指定されたセット (この場合は temp) を可能なすべての特権のセットから差し引いた結果です。11 行目を実行した結果、temp には、このプログラムが必要としない特権が含まれています。14 行目で、temp によって定義された不要な特権が、許可されたセットから差し引かれています。この削除の結果、それらの特権が実効セットからも削除されます。16 行目で、不要な特権が制限セットから削除されています。18 行目で、temp 変数は解放されています。というのも、temp は以後使用しないからです。
このプログラムは特権に対応しています。したがって、このプログラムでは setuid は使用されず、20 行目で実効 UID がユーザーの実際の UID にリセットされています。
22 行目では、file_dac_read 特権を実効セットから削除することで、この特権が無効化されています。実際のプログラムでは、file_dac_read が必要とされる前に、何らかの処理が行われます。このサンプルプログラムでは、25 行目でファイルを読み取る際に file_dac_read が必要となります。 したがって、24 行目で file_dac_read が有効化されています。 ファイルの読み取り後すぐに、file_dac_read が実効セットから再度削除されています。すべてのファイルの読み取りが完了すると、すべての特権セット内の file_dac_read をオフにすることで、file_dac_read が完全に削除されています。
次の表は、プログラム実行中の特権セットの遷移を示したものです。行番号が表示されています。
表 2–2 特権セットの遷移| ステップ | temp セット | 許可された特権セット | 実効特権セット | 制限特権セット | 
|---|---|---|---|---|
| 初期状態 | – | すべて | すべて | すべて | 
| 5 行目 – temp を基本特権に設定します。 | 基本 | すべて | すべて | すべて | 
| 7 行目 – file_dac_read を temp に追加します。 | 基本 + file_dac_read | すべて | すべて | すべて | 
| 9 行目 – proc_exec を temp から削除します。 | 基本 + file_dac_read – proc_exec | すべて | すべて | すべて | 
| 11 行目 – temp を反転します。 | すべて – (基本 + file_dac_read – proc_exec) | すべて | すべて | すべて | 
| 14 行目 – 許可されたセット内の不要な特権をオフにします。 | すべて – (基本 + file_dac_read – proc_exec) | 基本 + file_dac_read – proc_exec | 基本 + file_dac_read – proc_exec | すべて | 
| 16 行目 – 制限セット内の不要な特権をオフにします。 | すべて – (基本 + file_dac_read – proc_exec) | 基本 + file_dac_read – proc_exec | 基本 + file_dac_read – proc_exec | 基本 + file_dac_read – proc_exec | 
| 18 行目 – temp ファイルを解放します。 | – | 基本 + file_dac_read – proc_exec | 基本 + file_dac_read – proc_exec | 基本 + file_dac_read – proc_exec | 
| 22 行目 – 必要になるまで file_dac_read をオフにします。 | – | 基本 – proc_exec | 基本 – proc_exec | 基本 + file_dac_read – proc_exec | 
| 24 行目 – 必要に応じて file_dac_read をオンにします。 | – | 基本 + file_dac_read – proc_exec | 基本 + file_dac_read – proc_exec | 基本 + file_dac_read – proc_exec | 
| 27 行目 – read() 操作後にfile_dac_read をオフにします。 | – | 基本 – proc_exec | 基本 – proc_exec | 基本 + file_dac_read – proc_exec | 
| 29 行目 – 不要になった file_dac_read をすべてのセットから削除します。 | – | 基本 – proc_exec | 基本 – proc_exec | 基本 – proc_exec |