Go to main content
Oracle® Solaris 11 セキュリティー開発者ガイド

印刷ビューの終了

更新: 2016 年 11 月
 
 

特権を使用したプログラミング

このセクションでは、特権を操作するためのインタフェースについて説明します。特権プログラミングインタフェースを使用するには、次のヘッダーファイルが必要になります。

#include <priv.h>

また、特権付きアプリケーションにおける特権インタフェースの使用例も示します。

特権のデータ型

    特権インタフェースで使用される主なデータ型は、次のとおりです。

  • 特権型 – 個々の特権は、priv_t 型定義によって表現されます。priv_t 型の変数を特権 ID 文字列で初期化するには、次のようにします。

    priv_t priv_id = PRIV_FILE_DAC_WRITE;
  • 特権セット型 – 特権セットは、priv_set_t データ構造体によって表現されます。priv_set_t 型の変数を初期化するには、表 1に示す特権操作関数のいずれかを使用します。

  • 特権操作型 – ファイルまたはプロセスの特権セット上で実行される操作の種類は priv_op_t 型定義によって表現されます。すべての種類の特権セットですべての操作が有効とは限りません。詳細は、特権を使用したプログラミングの特権セットの説明を参照してください。

      特権操作として指定可能な値は、次のとおりです。

    • PRIV_ONpriv_set_t 構造体で表明された特権を、指定されたファイルまたはプロセスの特権セット内でオンにします。

    • PRIV_OFFpriv_set_t 構造体で表明された特権を、指定されたファイルまたはプロセスの特権セット内でオフにします。

    • PRIV_SET – 指定されたファイルまたはプロセスの特権セット内の特権を、priv_set_t 構造体で表明された特権に設定します。この構造体が空に初期化されていた場合、PRIV_SET は特権セットを none に設定します。

特権インタフェース

次の表は、特権を使用するためのインタフェースの一覧です。この表に続いて、主な特権インタフェースのいくつかについて説明します。

表 1  特権を使用するためのインタフェース
目的
関数
補足説明
特権セットの取得と設定
setppriv()getppriv() はシステム呼び出しです。priv_ineffect()priv_set() は簡易操作用のラッパーです。
特権の特定と変換
これらの関数は、指定された特権または特権セットを特定の名前または数字にマッピングします。
特権セットの操作
これらの関数は、特権のメモリー割り当て、テスト、および各種セット操作に関する機能を提供します。
プロセスフラグの取得と設定
PRIV_AWARE プロセスフラグは、プロセスが特権を理解するのか、それともプロセスがスーパーユーザーモデルの下で実行されるのかを示します。PRIV_DEBUG は特権のデバッグ時に使用されます。
低レベルの資格操作
これらのルーチンは、デバッグ、低レベルのシステム呼び出し、およびカーネル呼び出しを行う際に使用されます。

setppriv(): 特権設定用

特権設定用の主要関数は、setppriv() です。その構文は次のとおりです。

int setppriv(priv_op_t op, priv_ptype_t which, \
const priv_set_t *set);

    op は、実行する特権操作を表します。op パラメータには、次の 3 つの値のいずれかを指定できます。

  • PRIV_ONset 変数によって指定された特権を、which によって指定されたセット型に追加します

  • PRIV_OFFset 変数によって指定された特権を、which によって指定されたセット型から削除します

  • PRIV_SETset 変数によって指定された特権で、which によって指定されたセット型に含まれる特権を置き換えます

    which には、変更する特権セットの種類を指定します。次のいずれかを指定します。

  • PRIV_PERMITTED

  • PRIV_EFFECTIVE

  • PRIV_INHERITABLE

  • PRIV_LIMIT

set には、変更操作で使用される特権を指定します。

さらに、簡易関数 priv_set() が提供されています。

priv_str_to_set(): 特権マッピング用

これらの関数は、特権名を数値にマッピングする場合に役立ちます。priv_str_to_set() は、このファミリの一般的な関数です。priv_str_to_set() の構文は次のとおりです。

priv_set_t *priv_str_to_set(const char *buf, const char *set, \
const char **endptr);

priv_str_to_set() は特権名の文字列を引数に取りますが、その文字列は buf に指定されます。priv_str_to_set() が返す特権値のセットは、4 つの特権セットのいずれかと組み合わせることができます。**endptr は、構文解析エラーのデバッグ時に使用できます。

    buf には次のキーワードを含めることができます。

  • all」は、定義済みのすべての特権を示します。「all,!priv_name,...」の書式を使えば、指定された特権を除くすべての特権を指定できます。


    注 - priv_set, “!priv_name,...」を使用する構文は指定された特権のセットから指定された特権を取り去ります。最初にセットを指定せずに「!priv_name,...」を使用しないでください。元になる特権セットが指定されていないと、構文は空の特権セットから指定された特権を取り去ることになり、事実上、特権がないことになってしまいます。
  • none」は、特権なしを示します。

  • basic」 は、標準 UNIX オペレーティングシステムへのログイン時にすべてのユーザーに従来から許可されている操作を実行する際に必要となる特権のセットを示します。

特権のコーディング例

このセクションでは、スーパーユーザーモデルを使って特権を囲い込みする方法と、最小特権モデルを使って特権を囲い込みする方法を比較します。

スーパーユーザーモデルでの特権の囲い込み

次の例では、スーパーユーザーモデルで特権操作を囲い込みする方法を示します。

使用例 1  スーパーユーザー特権の囲い込み例
/* 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 の結果として、許可されたセットと実効セットは最初、すべての特権に設定されている。

  • 継承可能なセットは最初、基本特権に設定されている。

  • 制限セットは最初、すべての特権に設定されている。

コードリストに続いて、この例の説明があります。

使用例 2  最小特権の囲い込み例
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_exectemp から削除されています。これで、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  特権セットの遷移
手順
temp セット
許可された特権セット
実効特権セット
制限特権セット
初期状態
すべて
すべて
すべて
5 行目 – temp を基本特権に設定します。
基本
すべて
すべて
すべて
7 行目 – file_dac_readtemp に追加します。
基本 + file_dac_read
すべて
すべて
すべて
9 行目 – proc_exectemp から削除します。
基本 + file_dac_readproc_exec
すべて
すべて
すべて
11 行目 – temp を反転します。
すべて – (基本 + file_dac_readproc_exec)
すべて
すべて
すべて
14 行目 – 許可されたセット内の不要な特権をオフにします。
すべて – (基本 + file_dac_readproc_exec)
基本 + file_dac_readproc_exec
基本 + file_dac_readproc_exec
すべて
16 行目 – 制限セット内の不要な特権をオフにします。
すべて – (基本 + file_dac_readproc_exec)
基本 + file_dac_readproc_exec
基本 + file_dac_readproc_exec
基本 + file_dac_readproc_exec
18 行目 – temp ファイルを解放します。
基本 + file_dac_readproc_exec
基本 + file_dac_readproc_exec
基本 + file_dac_readproc_exec
22 行目 – 必要になるまで file_dac_read をオフにします。
基本 – proc_exec
基本 – proc_exec
基本 + file_dac_readproc_exec
24 行目 – 必要に応じて file_dac_read をオンにします。
基本 + file_dac_readproc_exec
基本 + file_dac_readproc_exec
基本 + file_dac_readproc_exec
27 行目 – read() 操作後にfile_dac_read をオフにします。
基本 – proc_exec
基本 – proc_exec
基本 + file_dac_readproc_exec
29 行目 – 不要になった file_dac_read をすべてのセットから削除します。
基本 – proc_exec
基本 – proc_exec
基本 – proc_exec