特権とは、セキュリティ関連の権限を個別のグループにまとめたものです。それぞれのグループ (特権) は、それぞれ 1 つのセキュリティ関連の処理に対応しています。特権を使用すると、プログラムは、通常システムセキュリティポリシーによって禁止されている特定の処理を実行できます。必須または任意の適切なアクセスのないファイルやディレクトリにプログラムがアクセスすることを「禁止された処理」と言います。
プログラムは、1 つのセキュリティ関連の処理を実行するために、1 つ以上の特権をオン (有効) にします。たとえば、プログラムにあるファイルへの必須書き込みアクセスがない場合、このプログラムは file_mac_write 特権をオンにします。任意書き込みアクセスもない場合、同様に file_dac_write 特権をオンにします。しかし、このプログラムに必須書き込みアクセスと任意書き込みアクセスの両方がある場合、特権は必要ありません。ほとんどのプログラムは、システムセキュリティポリシーの範囲内で動作するため、特権を必要としません。
この章では、特権を処理するプログラミングインタフェースについて説明します。
Trusted Solaris 7 では、最大 128 種類の特権が使用できます。この合計数は、次に示す Trusted Solaris 7 の特権とサイトが定義する特権を含みます。Trusted Solaris 7 の特権の詳細は、priv_desc(4) を参照してください。
ファイルシステムの特権は、ユーザー ID、グループ ID、アクセス権、ラベル付け、所有権、ファイル特権セットに対するファイルシステムの制限を無効にします。
System V プロセス間通信 (IPC) 特権は、メッセージ待ち行列、セマフォセット、共有メモリー領域に対する制限を無効にします。
ネットワーク特権は、予約されたポートの割り当て、マルチレベルポートの割り当て、ブロードキャストメッセージの送信、メッセージまたは通信終端へのセキュリティ属性の指定に対する制限を無効にします。
プロセス特権は、プロセス監査、ラベル付け、秘密チャネル対策、所有権、認可上限、ユーザー ID、グループ ID に対する制限を無効にします。
システム特権は、システム監査、ワークステーションのブート、ワークステーションの構成管理、コンソール出力の切り替え、デバイス管理、ファイルシステム、ディレクトリに対するハードリンクの作成、メッセージ待ち行列サイズの拡大、プロセスの拡大、ワークステーションのネットワーク構成、他社のロード可能なモジュール、ラベル変換に対する制限を無効にします。
X ウィンドウシステム特権は、ウィンドウからの読み取り、ウィンドウに対する書き込み、入力デバイス、ラベル付け、フォントパス、ウィンドウ間のデータ移動、X サーバーの資源管理、ダイレクトグラフィックスアクセス (DGA) に対する制限を無効にします。
特権は、ファイル特権セットとプロセス特権セットで構成されます。
実行可能ファイル、インタプリタ処理されるファイル、CDE アクションには、setfpriv(1) を使用してファイルマネージャによって割り当てられたファイル特権セット、またはほかの特権プログラムによって割り当てられたファイル特権セットがあります。このファイル特権セットは、強制された特権セット (強制セット) であり、かつ許容された特権セット (許容セット) です。
許容された特権セット (以下許容セット) には、実行可能ファイル (強制されたファイルセット) に対して割り当てられる特権、または実行するプロセスによって継承されて使用される特権が含まれます。プロセスが別のプロセスから特権を継承する場合、その特権が実行可能ファイルの許容セットに存在しないかぎり、プロセスはその特権を使用できません。
許容された特権は、「トロイの木馬」型保護を提供し、システムに入りこんで別のプロセスから特権を継承するトラステッド以外のプロセスからシステムを保護します。特権の継承の詳細は、「継承可能な特権セット」を参照してください。
強制された特権のセット (以下強制セット) には、あらゆるユーザーが実行するセキュリティ関連処理の実行を開始する場合にプログラムが必要とする特権が含まれます。強制された特権のあるコマンドは、どのシェルからも起動できます。また、強制された特権のある CDE アクションは、どのワークスペースからも起動できます。強制セットは、常に、許容セットに等しいか、あるいは許容セットのサブセットにします。そのため、強制セット内の特権はすべて、許容セット内にも存在します。
インタプリタ処理されるファイルは「#!」から始まるスクリプトであり、実行のためにインタプリタに読み取られます。スクリプトファイルとインタプリタは、それぞれ強制された特権セットと許容された特権セットを持つことができます。最終的な強制セットは、スクリプトに割り当てられた強制セットと、インタプリタに割り当てられた、インタプリタの許容セットによって制限される強制セットの組み合わせになります。
実行プロセスは、ファイルセットの内容と呼び出しプロセスから継承されるあらゆる特権に基づくアルゴリズムから計算されるプロセス特権セットを持ちます。プロセス特権セットには、継承可能な特権セット、保存された特権セット、許可された特権セット、有効な特権セットがあります。
親プロセスから受け取った特権がある場合、それらの特権は継承可能な特権セット (以下、継承可能セット) に含まれます。プロセスは、exec(1) で新しいプログラムを実行するときや、fork(2) で新しいプロセスを実行するときに継承可能セットを渡します。新しいプログラムやプロセスの継承可能セットは、常に呼び出しプロセスの継承可能セットと同じものになります。新しいプログラムやプロセスが継承された特権を使用できるのは、その特権が実行可能ファイルの許容セット内にも存在する場合にかぎります。ただし、別の新しいプログラムまたはプロセスにすべての継承可能な特権が渡されます。プログラムは、fork() や exec() に先行して、その継承可能セットをクリアし、許可セット内の特権をどれでも継承可能セットに加えることができます。
システム管理者は、継承可能セットを実行プロファイル内の CDE アクションやコマンドに割り当てることができます。特権は、実行プロファイルが割り当てられたユーザーまたは役割が CDE アクションを開始するとき、またはプロファイルシェルからコマンドを実行するときに継承されます。
強制された特権がプロセスの許可セット内に存在する場合、そのプロセスはプロセス自体の継承可能セットに強制された特権を設定し、その強制された特権を新しいプロセスまたはプログラムに渡すことができます。
保存されたセット (以下、保存セット) は、プロセスに対して使用が許容されている継承された特権のコピーです。保存セットは、許容セットによって制限された継承可能セットと同じものになります。許容セットにも存在する継承可能セット内の特権は、保存セットに置かれます。保存セットを変更するインタフェースはありません。
プログラムは、その保存セットを照会して、元の特権を確認できます。特権が保存セットに存在する場合、プログラムが起動されるとその特権は継承されます。特権が保存セットに存在しない場合、プログラムが起動されるとその特権は強制されます。
通常のユーザー (強制された特権) によって開始されたプロセスはセキュリティ関連処理のための動作がワークステーションの範囲に制限される場合があります。一方、管理役割のある承認を受けたユーザー (継承された特権) によって開始されたプロセスは、同じセキュリティ関連処理を行うためにより広く、ネットワークの範囲で動作する場合があります。
許可された特権セット (以下、許可セット) には、プロセスが使用できる強制された特権と継承された特権が含まれます。許可セットは、強制セットと、許容セットによって制限される継承可能セットを合わせたものです。許容セットにも存在する継承可能セット内の特権は、強制セットと結合され、許可セット内に置かれます。特権プロセスは、ゼロではない許可セットを使用したプロセスです。
特権は許可セットから削除できますが、追加はできません。許可された特権がいったん削除されると、元に戻すことも、継承可能セットに加えることもできません。また、許可セットから削除される前にその特権が継承可能セットに加えられている場合には、継承可能セットからも削除されます。
セキュリティ上の予防手段として、許可セットからプログラムが使用する可能性のない特権を削除できます。それにより、実行可能ファイルに不正に割り当てられたか、誤って継承された許容特権をプログラムが使用することはありません。
有効特権とは、プロセスが 1 つのセキュリティ関連処理のために使用する許可された特権を意味します。デフォルトでは、有効な特権セット (以下、有効セット) は、初めは許可セットと同じです。しかし、プログラムは、特権のブラケット化を行うために、実行されるとすぐに有効セットをオフにする必要があります。
特権のブラケット化とは、有効な特権セットをオフにしたあと、一定のセキュリティ関連処理に必要な特権だけをオン (有効) にし、不要になった時点ですぐにそれらの特権をオフにすることを言います。「有効な特権のブラケット化」を参照してください。
setuid(2)、setreuid(2)、または seteuid(2) システムコールが呼び出されると、有効な特権セットが保存セットにコピーされ、有効セットはクリアされます。有効セットが必要な場合は、保存セットからコピーして戻すか、戻す必要のある有効な特権をオンにします。元の特権を確認するなどの理由で最初の保存セットが必要な場合は、初めにテストをしておくか、保存セットをコピーします。
ユーザー ID の変更が行われている間は初めの呼び出し元のプロセスに与えられた特権をプロセスは使用できないという原則に基づいて、有効セットはクリアされます。この時点でも、setuid プログラムは、許可セットの特権を有効セットに入れることによって特権を処理できます。
特権を持つアプリケーションはすべて、トラステッドコンピューティングベース (TCB) の一部です。特権を持つアプリケーションの中には、1 つ以上の強制された特権を持つものがあり、これらは特権を継承する場合もしない場合もあります。この種のアプリケーションとして、標準 UNIX システムにおける、Trusted Solaris 7 と同等の setuid アプリケーションがあります。
特権を持つアプリケーションの中には強制された特権を持たないアプリケーションもあり、それらは常に呼び出し元のプロセスから特権を継承します。この種のアプリケーションは、常に特権プロセスから呼び出されます。
priv_desc(4) のマニュアルページには、すべてのシステム特権の名称、明示的定数名、説明が記載されています。
ファイル特権セットの取得と設定を行うシステムコールは、ファイルに対する必須アクセスと任意アクセスを必要とし、アクセスが拒否される場合は特権が必要な場合があります。詳細は、getfpriv(2) のマニュアルページを参照してください。
setfpriv(1) と fsetfpriv(2) システムコールを使用してファイル特権セットを設定するには、file_setpriv 特権が必要です。
プロセスが実行可能ファイルに情報を書き込む場合、ファイルの強制された特権セットと許容された特権セットが none に設定されることを防ぐには、file_setpriv 特権が必要です。
特権プロセスがコアファイルを作成する場合、proc_dumpcore 特権が有効 (オン) でなければなりません。これは、特権プロセスからのコアファイルには機密度の高い情報が含まれている可能性が高いためです。この特権をオフにすれば、コアファイルは作成されません。デバッグのために、プロセスの開始から完了までオンにする以外は、この特権はオフにしておくべきです。
呼び出し元のプロセスは、ユーザー ID、グループ ID、追加グループ ID のいずれかを変更する場合、有効セット内に proc_setid 特権を必要とします。
特権を持つアプリケーションの開発は、運用中の Trusted Solaris システムとは別の、独立した保護環境で行う必要があります。未完成の特権を持つアプリケーションは本質的に信頼できないため、稼動しているシステムのセキュリティに害を及ばないようにする必要があります。そのためには、あらゆる特権アプリケーションについて、以降で紹介する内容を実行することをお勧めします。
安全なアプリケーションのパッケージ化についての詳細は、付録 B 「Trusted Solaris 7 インタフェースリファレンス」を参照してください。
アプリケーションが特権を使用する場合、システムセキュリティポリシーが侵害されます。特権を持つ処理はブラケット化し、機密情報に害が及ばないように制御してください。特権をブラケット化する方法の詳細は 「有効な特権のブラケット化」を参照してください。
アプリケーション内でシェルエスケープを使用すると、エンドユーザーが信頼性を損ねる場合があります。たとえば、メールアプリケーションの中には !command 行をコマンドとして解釈して実行するものがあります。メールアプリケーションがトラステッドプロセスである場合、特権を使用して動作します。エンドユーザーは、この機能を使用し、メールアプリケーションの特権を利用するスクリプトを作成できます。アプリケーションがトラステッド環境で動作する場合は、シェルエスケープの機能を除いてください。
アプリケーションに特権が与えられている場合、アプリケーションをコマンド行から直接実行することは避けてください。これは、エンドユーザーに特権を利用される可能性があるためです。たとえば、コマンドに続けて文書名の入力をエンドユーザーに許すアプリケーションが多く見受けられます。このようなアプリケーションに必須アクセス制御を無効にする特権が与えられていると、アプリケーションが外部アプリケーションに書き込む必要がある場合、通常は表示する特権がない文書をエンドユーザーが開いてしまう可能性があります。
特権アプリケーションの秘密チャネルを見つけ出して除いてください。秘密チャネルとは、意図されていないパスのことをいいます。秘密チャネルが利用されると、必須アクセス制御によって保護されずに情報が転送されます。たとえば、特権を持つマルチラベルのクライアントサーバーアプリケーションで、サーバーにサービス要求の待ち行列があるとします。特権のないクライアントがこの待ち行列に対し要求の追加や削除ができ、待ち行列のサイズに上限がある場合は、待ち行列の状態がいっぱいかそうでないかという情報が、秘密チャネルとして利用される可能性があります。
この章で説明しているプログラミングインタフェースを使用するには、次のヘッダーファイルが必要です。
#include <tsol/priv.h>
この章の例は、次のライブラリを使用してコンパイルしています。
-ltsol
1 つの特権は、priv_t 型の定義で表します。priv_t 型の変数は、定数名または数値 ID である特権 ID で初期化します。コードの読みやすさの点から、定数名を使用するとよいでしょう。
priv_t priv_id = PRIV_FILE_DAC_WRITE;
特権セットは、priv_set_t データ構造体で表します。priv_set_t 型の変数は、str_to_priv_set(3) ルーチンまたは PRIV_ASSERT マクロを使用して初期化します。これは、特権 ID (PRIV_ASSERT) を使用して一度に 1 つの特権を表明するか、それともインタフェース str_to_priv_set を使用して 1 つ以上の特権の文字列を特権セットに変換するかによります。
ファイル特権セットの種類は、priv_ftype_t 型の定義で表します。値は、PRIV_ALLOWED と PRIV_FORCED です。
プロセス特権セットの種類は、priv_ptype_t 型の定義で表します。値は、PRIV_EFFECTIVE、PRIV_INHERITABLE、PRIV_PERMITTED、PRIV_SAVED です。
ファイルまたはプロセスの特権セットで実行する操作の種類は、priv_op_t 型の定義で表します。特権セットの種類によって、有効な操作は異なります。「特権セット」を参照してください。
値を次に示します。
PRIV_ON - priv_set_t 構造体で表明された特権を、指定されたファイルまたはプロセスの特権セットでオンにします。
PRIV_OFF - priv_set_t 構造体で表明された特権を、指定されたファイルまたはプロセスの特権セットでオフにします。
PRIV_SET - 指定されたファイルまたはプロセスの特権セット内の特権を、priv_set_t 構造体で表明された特権に設定します。構造体が空に初期化されている場合は、PRIV_SET はその特権セットを none に設定してクリアします。
特権マクロは、単一特権と特権セットで動作します。特権マクロの詳細は、priv_macros(5) のマニュアルページで説明しています。マクロは、ファイルやプロセスに関連付けられた特権セットを直接変更しませんが、priv_set_t 型の変数を操作します。
特権マクロ |
内容 |
PRIV_ASSERT(priv_set, priv_id) |
特権 (priv_id) をセット (priv_set) に入れる。 |
PRIV_ISASSERT(priv_set, priv_id) |
特権 (priv_id) がセット (priv_set) で表明されている場合、0 以外を返す。 |
PRIV_EQUAL(priv_set_a, Priv_set_b) |
両方のセットが同じである場合、0 以外を返す。 |
PRIV_EMPTY(priv_set) |
セットを空に初期化する。 |
PRIV_FILL(priv_set) |
セットにすべての特権を入れる。 |
PRIV_ISEMPTY(priv_set) |
セットが空の場合 0 以外を返し、空でない場合 0 を返す。 |
PRIV_ISFULL(priv_set) |
特権がシステムに定義されたすべての特権を含む場合、0 以外を返す。それ以外の場合、0 を返す。 |
PRIV_CLEAR(priv_set, priv_id) |
セット (priv_set) から特権 (priv_id) を削除する。 |
PRIV_INTERSECT(priv_set_a, priv_set_b) |
set_a と set_b の論理積を set_b に格納する。 |
PRIV_INVERSE(priv_set) |
priv_set の反転を priv_set に格納する。 |
PRIV_UNION(priv_set_a, priv_set_b) |
set_a と set_b の論理和を set_b に格納する。 |
PRIV_XOR(priv_set_a, priv_set_b,) |
set_a と set_b の排他的論理和を set_b に格納する。 |
PRIV_ISSUBSET(priv_set_a, priv_set_b) |
priv_set_a に表明されたすべての特権が priv_set_b でも表明されている場合に、0 以外を返す。それ以外の場合、0 を返す。 |
PRIV_TEST(priv_id, errno) |
priv_id が有効セット内に存在するかをテストし、true の場合 errno を 1 に設定し、false の場合 0 に設定する。 |
次に、ファイルとプロセスの特権セットを処理するインタフェースを示します。パス名を使用してファイルにアクセスするインタフェースと、ファイル記述子を使用してファイルにアクセスするインタフェースが存在する場合、両方の構文はほとんど同じであるため、例ではパス名インタフェースだけを示します。
これらのシステムコールは、ファイルとプロセスの特権セットを取得して設定します。
これらのシステムコールは、ファイルのフルパス名を使用して、ファイル特権セットの取得と設定を行います。getfpriv(2) のマニュアルページを参照してください。
int getfpriv( char *path, priv_ftype_t type, priv_set_t *priv_set); int setfpriv( char *path, priv_op_t op, priv_ftype_t type, priv_set_t *priv_set);
これらのシステムコールは、ファイル記述子を使用して、ファイル特権セットの取得と設定を行います。getfpriv(2) のマニュアルページを参照してください。
int fgetfpriv( int fd, priv_ftype_t type, priv_set_t *priv_set); int fsetfpriv( int fd, priv_op_t op, priv_ftype_t type, priv_set_t *priv_set);
これらのシステムコールは、プロセス特権セットの取得と設定を行います。getppriv(2) のマニュアルページを参照してください。
int getppriv( priv_ptype_t type, priv_set_t *priv_set); int setppriv( priv_op_t op, priv_ptype_t type, priv_set_t *priv_set);
プロセス特権セットに対するアクセスは、次に示すライブラリルーチンを使用しても可能です。構文は少々異なりますが、意味は同じです。
次に、プロセス特権セットを取得するライブラリルーチン、特権 ID または特権セットをバイナリと ASCII 間で変換するライブラリルーチン、指定された特権 ID の特権説明テキストを取得するライブラリを示します。
これらのライブラリルーチンは、プロセスに対する有効な特権セット、許可された特権セット、継承可能な特権セットを設定します。set_effective_priv(3) のマニュアルページを参照してください。
int set_effective_priv( priv_op_t op, int privno, priv_t priv_id); int set_permitted_priv( priv_op_t op, int privno, priv_t priv_id); int set_inheritable_priv( priv_op_t op, int privno, priv_t priv_id);
プロセス特権セットに対するアクセスは、setppriv(2) と getppriv(2) を使用しても可能です。構文は少し異なりますが、意味は同じです。
これらのライブラリルーチンは、特権 ID または特権セットをバイナリと ASCII 間で変換します。priv_to_str(3) のマニュアルページを参照してください。
char* priv_to_str( const priv_t priv_id); priv_t str_to_priv( const char *priv_name); char* priv_set_to_str( priv_set_t *priv_set, const char sep, char *buf, int *blen); char* str_to_priv_set( const char *priv_names, priv_set_t *priv_set, const char *sep);
これらのライブラリルーチンは、指定された特権 ID の特権テキストを取得します。priv_to_str(3) のマニュアルページを参照してください。
char* get_priv_text(const priv_t priv_id);
これらのライブラリルーチンは、指定された特権 ID を対応する外部名または数値 ID に変換し、再び元に戻します。これらのルーチンは、priv_names(4) のマニュアルページで説明している特権名のデータベースファイルを読み取り、priv_id (特権 ID) と *string (文字列) 間で変換を行います。
この例で priv_id は、明示的定数名 PRIV_FILE_DAC_WRITE に初期化され、priv_to_str(3) ルーチンに渡されて外部名に変換されます。
この節のコードセグメントのヘッダーファイルと宣言は、最初のプログラムに示されています。
#include <tsol/priv.h> main() { priv_t priv_id = PRIV_FILE_DAC_WRITE; char *string; string = priv_to_str(priv_id); printf("Priv string = %s¥n", string); }
printf 文によって、次のように出力されます。
Priv string = file_dac_write
次の例で、priv_to_str(3) ルーチンから返される string は、str_to_priv(3) ルーチンに渡されて、文字列から数値 ID に変換されます。
priv_id = str_to_priv(string); printf("Priv ID = %d¥n", priv_id);
printf 文によって、次のように出力されます。
Priv ID = 6
get_priv_text(3) ルーチンは、指定された priv_id の説明テキスト (特権について説明するテキスト) を返します。priv_names(4) のマニュアルページには、システム内のすべての特権の説明テキストが示されています。
string = get_priv_text(priv_id); printf("%s¥n", string);
printf 文によって、次のように出力されます。
Allows a process to write a file or directory whose permission bits or ACL do not allow the process write permission. |
この節では、Trusted Solaris 7 に装備されている、実行可能ファイルの特権セットの設定と取得を行うユーザーコマンドとプログラミングインタフェースについて説明します。強制特権と許容特権が設定されていない場合、デフォルトでは強制特権セットと許容特権セットには none が入っています。
実行前にファイル特権セットを設定した場合、その新しい特権セットは、その時点から有効になり、現在の実行に対するプロセス特権セットの計算に使用されます。実行中にファイル特権セットを設定した場合、次の実行までは無効で、現在の実行に対するプロセス特権セットには影響を与えません。
コマンド行からファイル特権セットの設定と取得を行うには、setfpriv(1) と getfpriv(1) を使用します。setfpriv(1) は、file_setpriv 特権を必要とするので、このコマンドをプロファイルシェルから実行するときに、この特権を使用します。setfpriv(1) をスクリプトで使用する方法の詳細は、「スクリプトを使用したファイル特権の割り当て」を参照してください。
このコマンド行は、この章の例の executable (実行可能ファイル) に対して、ファイル特権セットを設定します。複数の特権を指定する場合は、スペースを入れずにコンマで区切ります。スペースを使用したい場合は、("privilege1, privilege2") のように特権名を二重引用符で囲んでください。
phoenix% setfpriv -s -f file_setpriv -a file_mac_write,proc_setid,file_setpriv executable
このコマンド行は、ファイル特権セットが設定されているかどうかの確認のため出力します。
phoenix% getfpriv executable executable FORCED: file_setpriv ALLOWED: file_mac_write,file_setpriv,proc_setid
この節で説明する特権マクロとシステムコールは、ファイル特権セットの取得と設定を行います。次のプログラムには、この章のすべての例で使用されるヘッダーファイルと変数宣言が含まれます。また、execfile (実行可能ファイル) に対するファイル特権セットの設定と取得を行うコードも含まれます。この execfile は、後で exec() により使用され、プロセス特権セットに何が起きるかを表示します。
setfpriv(1) システムコールは、execfile に強制特権セットと許容特権セットを設定し、file_setpriv 特権を要求します。file_setpriv 特権は executable の強制セットに含まれるため、この特権は実行中に許可セットで使用できます。デフォルトでは、有効セットは許可セットと同じです。また、すべての有効特権は、特権のブラケット化の準備のために明示的にオフにされるまでは、オンのままです。このコードにおける file_setpriv の使用は、特権のブラケット化 (「有効な特権のブラケット化」を参照) が有効にならないかぎり、セキュリティガイドラインに従いません。
/* cc priv.c -o executable -ltsol */ #include <tsol/priv.h> #include <sys/types.h> #include <errno.h> #include <stdio.h> /* 大域変数*/ extern int errno; char buffer [3*1024]; main() { char *priv_names = "file_mac_write$proc_setid"; char *string; char *privilege; char *file = "/export/home/zelda/executable"; char *execfile = "/export/home/zelda/execfile"; priv_set_t priv_set, priv_get, permitted_privs, saved_privs; int length = sizeof(buffer); int retval; pid_t pid; /* 後で exec() に使用する */ char *argv[8] = {"execfile"}; /* 特権セットのデータ構造を初期化する */ PRIV_EMPTY(&priv_get); PRIV_EMPTY(&priv_set); /* 許容された特権をオフ(無効) にする。詳細は本文を参照 */ retval = setfpriv(execfile, PRIV_SET, PRIV_ALLOWED, &priv_get);
/* 特権セット構造体内で、Priv_names に指定した特権を */ /* 表明し、execfile に割り当てる。特権の表明のやり方については */ /* 本文を参照 */ if((string = str_to_priv_set(priv_names, &priv_set, "$")) != NULL) printf("string = %s errno = %d¥n", string, errno); retval = setfpriv(execfile,PRIV_ON, PRIV_ALLOWED, &priv_set); /* 許容された特権セットに特権が含まれていることを確認する */ retval = getfpriv(execfile, PRIV_ALLOWED, &priv_get); priv_set_to_str(&priv_get, '$', buffer, &length); printf("execfile Allowed = %s¥n", buffer); /* 特権セットのデータ構造体を初期化する */ PRIV_EMPTY(&priv_set); PRIV_EMPTY(&priv_get); /* 特権セット構造体で file_mac_write を表明する */ PRIV_ASSERT(&priv_set, PRIV_FILE_MAC_WRITE); /* 強制された特権セットを execfile ファイルに対して有効にする */ retval = setfpriv(execfile, PRIV_ON, PRIV_FORCED, &priv_set); /* 強制された特権セットに特権が含まれていることを確認する */ retval = getfpriv(execfile, PRIV_FORCED, &priv_get); priv_set_to_str(&priv_get, `$', buffer, &length); printf("execfile Forced =%s¥n", buffer); }
printf 文により、execfile に対するファイル特権セットが次のように出力されます。
execfile Allowed = file_mac_write$proc_setid
execfile Forced = file_mac_write
この出力は、ドル記号 (「$」) を使用して許容される特権を分離しています。この区切り文字は、priv_set_to_str(3) に対する呼び出しで指定されます。セット内に特権が 1 つしかない場合には、区切り文字は使用しません。
強制セットは、許容セットのサブセットです。強制セット内のすべての特権は、許容セットがクリアされるときにクリアされます。許容セットはデフォルトでは none ですが、何も設定されていない状態から開始することがわかるように、初めにこれをクリアする習慣をつけるとよいでしょう。強制セットを設定する場合は、設定前に必ず許容セットのクリアと設定を行なってください。次のコードを実行すると、実行後、許容セットと強制セットは両方とも none になります。
PRIV_EMPTY(&priv_set); retval = setfpriv(execfile, PRIV_SET, PRIV_ALLOWED, &priv_set);
特権セット構造で特権を表明するには、PRIV_ASSERT マクロまたは str_to_priv_set(3) ルーチンを使用できます。表明する特権が 2 つ以上ある場合には、1 つの文で表明できる str_to_priv_set() が便利です。一方、PRIV_ASSERT は、セット内で表明する特権ごとに呼び出す必要があります。このコードは、許容セットに対し str_to_priv_set() ルーチンを使用し、強制セットに対し PRIV_ASSERT を使用しています。str_to_priv() ルーチンは、成功時には NULL を返し、失敗時には priv_names で渡した文字列を返します。
if((string = str_to_priv_set(priv_names, &priv_set, "$")) != NULL) printf("string = %s errno = %d¥n", string, errno); PRIV_EMPTY(&priv_set); PRIV_ASSERT(&priv_set, PRIV_FILE_MAC_WRITE);
次の例は、プロセス特権セットを操作します。これらの操作を実行する前に、ファイルとプロセスの特権セットを確認してください。プロセスセットは、「プロセス特権セット」のアルゴリズムから計算されます。
executable Allowed = file_mac_write$file_setpriv$proc_setid
executable Forced = file_setpriv
Permitted = file_mac_write$file_setpriv$proc_setid
Effective = file_mac_write$file_setpriv$proc_setid
Saved = file_mac_write$proc_setid
Inheritable = file_mac_write&file_setpriv$proc_setid
デフォルトで、有効な特権はオンに設定され許可セットと一致します。特権のブラケット化は、この有効特権をオフにし、続いて一定のインタフェース呼び出しに必要な許可された特権だけをオン (有効) にし、特権呼び出しが完了したときにそれらをオフにする、という手順で行われます。
ほかのプロセスに特権を与えることにより、特権プロセスを不正に利用することはできません。
不要な場合には特権をオフにすることで、アプリケーションコード中の問題のために特権が誤って使用されることを防止できます。
プロセスは現在呼び出しているインタフェースに必要な特権しか使用しないため、最小限の特権の原則が実施されます。
特権のブラケット化によりコードの評価時に特権の正確な使用位置がわかるため、トラステッドアプリケーションの評価は比較的容易です。
インタフェースにどの特権が必要かを分析する場合、そのインタフェースのマニュアルページに説明されているインタフェースの動作と特権の目的を参照してください。特権の中には、ほかの特権と比べ「影響力が大きい」ものがあります。そのような特権は、扱いに十分気をつける必要があります。
影響力が大きい特権とは、必須または任意のアクセス制御ポリシーを無効にする特権を言います。
影響力が小さい特権とは、ファイルシステムのマウントのような制限された操作を可能にするものです。
たとえば、コードセグメントを調べ、そのコードが mount(1M) システムコールで特権を使用していることを確認し、その特権の使用が何らかの形で不正に利用される可能性があるかについては、比較的容易に判断できます。しかし、制限されたファイルにアクセスする必須または任意のアクセスポリシーを無効にする特権の使用が、不正に利用される可能性があるか判断するのは困難です。
コード内で特権のブラケット化を実行し正しく動作させるのは、開発者の責任で行います。特権は、トラステッド以外のプロセスには許されていない、ポリシーの無効化を行うことができます。この点に十分注意して、特権を扱うようにしてください。
この節では、setfpriv(1) システムコールのブラケット化の手順と、有効セットに対するブラケット化の効果の概要を示します。コードは、次の節で示します。
ブラケット化を行う前の実行開始時には、許可セットと有効セットには次の特権が含まれています。
Permitted = file_mac_write$file_setpriv$proc_setid
Effective = file_mac_write$file_setpriv$proc_setid
アプリケーションの開始時に有効セットをクリアします。
Permitted = file_mac_write$file_setpriv$proc_setid
Effective = none
setfpriv() システムコールをブラケット化します。
setfpriv() システムコールを呼び出す直前に、有効セット内で file_setpriv 特権をオンにします。
Permitted = file_mac_write$file_setpriv$proc_setid
Effective = file_setpriv
setfpriv() システムコールの直後に、有効セットをオフにします。
Permitted = file_mac_write$file_setpriv$proc_setid
Effective = none
この例は、set_effective_priv(3) を使用して、アプリケーションの開始時に有効セットをクリアします。PRIV_SET パラメータは、有効特権セットをクリアします。ゼロ (0) は、特権 ID のパラメータリストが存在しないことを意味します。
if (set_effective_priv(PRIV_SET, 0) == -1) perror("Cannot clear effective privileges");
すべての有効特権セットをオフにした後、特権が必要になるまでアプリケーションコードを継続します。
この例は、set_effective_priv(3) を使用してブラケット化を行なっています。最初の呼び出しは、有効セット内で file_setpriv 特権をオンにして (表明して) います。2 番目の呼び出しはその特権をオフにします。1 は、特権パラメータリストに、特権定数が 1 つ (PRIV_FILE_SETPRIV) 含まれることを意味します。
/* 有効セット内で file_setpriv を表明 (オン) にする */ if (set_effective_priv(PRIV_ON, 1, PRIV_FILE_SETPRIV) == -1) perror("Cannot assert PRIV_FILE_SETPRIV"); /* インタフェースを呼び出す */ retval = setfpriv(execfile, PRIV_SET, PRIV_ALLOWED, &priv_get); /* file_setpriv 特権をオフ (無効) にする */ if (set_effective_priv(PRIV_OFF, 1, PRIV_FILE_SETPRIV) == -1) perror("Cannot clear PRIV_FILE_SETPRIV"); /* アプリケーションコードを続ける */
次に、setfpriv(1) をブラケット化すべき位置を示すコメントが入った、アプリケーションコードの本体の例を示します。
PRIV_EMPTY(&priv_get); PRIV_EMPTY(&priv_set); /* 有効セット内で file_setpriv をオン (有効) にする */ retval = setfpriv(execfile, PRIV_SET, PRIV_ALLOWED, &priv_get); /* file_setpriv 特権をオフ (無効) にする */ if((string = str_to_priv_set(priv_names, &priv_set, "$")) != NULL) printf("string = %s errno = %d¥n", string, errno); /* 有効セット内で file_setpriv をオン (有効) にする */ retval = setfpriv(execfile,PRIV_ON, PRIV_ALLOWED, &priv_set); /* file_setpriv 特権をオフ (無効) にする */ retval = getfpriv(execfile, PRIV_ALLOWED, &priv_get); priv_set_to_str(&priv_get, '$', buffer, &length); printf("execfile Allowed = %s¥n", buffer); PRIV_EMPTY(&priv_set); PRIV_EMPTY(&priv_get); PRIV_ASSERT(&priv_set, PRIV_FILE_MAC_WRITE); /* 有効セット内で file_setpriv をオン (有効) にする */ retval = setfpriv(execfile, PRIV_ON, PRIV_FORCED, &priv_set); /* file_setpriv 特権をオフ (無効) にする */ retval = getfpriv(execfile, PRIV_FORCED, &priv_get); priv_set_to_str(&priv_get, `$', buffer, &length); printf("execfile Forced =%s¥n", buffer);
アプリケーションは、許可特権セットをチェックし、動作に必要なすべての特権があることを確認できます。チェックを行うと、アプリケーションに特権が足りない場合は、その結果に対しエラーメッセージを発行できます。必要な特権がそろわない状態で継続すると、エラーメッセージの解釈がより困難になります。
次の例は、許可セットを取得し、PRIV_FILE_MAC_WRITE、PRIV_PROC_SETID、および PRIV_FILE_SETPRIV をチェックします。この例では示していませんが、PRIV_ISSUBSET マクロを使用すると、1 つの特権セットに別の特権セット内のすべての特権が含まれるかどうかを調べることもできます。
/* 特権セットのデータ構造体を初期化する */ PRIV_EMPTY(&permitted_privs); /* 許可されたセットの特権を調べる */ if (getppriv(PRIV_PERMITTED, &permitted_privs) == -1) perror("Cannot get list of permitted privileges¥n"); if (!PRIV_ISASSERT(&permitted_privs, PRIV_FILE_MAC_WRITE)) fprintf(stderr, "Need: file_mac_write.¥n"); if (!PRIV_ISASSERT(&permitted_privs, PRIV_PROC_SETID)) fprintf(stderr, "Need: proc_setid.¥n"); if (!PRIV_ISASSERT(&permitted_privs, PRIV_FILE_SETPRIV)) fprintf(stderr, "Need: file_setpriv.¥n");
許可セットから特権を削除することは可能ですが、特権はいったん削除すると元に戻すことはできません。継承可能セットには、許可セット内の特権しか含めることができません。そのため、継承可能セットに必要な許可特権は削除しないでください。この例は、許可セットから file_mac_write 特権を削除します。1 は、パラメータリストに特権定数が 1 つ含まれることを示します。
if(set_permitted_priv(PRIV_OFF, 1, PRIV_FILE_MAC_WRITE) == -1) perror ("Cannot remove file_mac_write from permitted set");
この呼び出し以前には、許可セットに次の特権が含まれます。
executable Permitted = file_mac_write$file_setpriv$proc_setid
呼び出し後は、許可セットに次の特権が含まれます。
executable Permitted = file_setpriv$proc_setid
アプリケーションは、保存特権セットをチェックして特権の発生元を確認し、その結果に基づいて、作業を実行できます。この例は、保存セットを取得し、PRIV_PROC_SETID と PRIV_FILE_SETPRIV をチェックします。その結果、file_setpriv 特権は継承されず、proc_setid 特権が継承されていることを確認します。
PRIV_EMPTY(&saved_privs); if (getppriv(PRIV_SAVED, &saved_privs) == -1) perror("Cannot get list of saved privileges¥n"); if (!PRIV_ISASSERT(&saved_privs, PRIV_PROC_SETID)) fprintf(stderr, "proc_setid not in saved set. ¥n"); if (!PRIV_ISASSERT(&saved_privs, PRIV_FILE_SETPRIV)) fprintf(stderr, "file_setpriv not in saved set.¥n");
fork によるプロセスまたは exec によるプログラムの継承可能セットに含めたい特権が継承可能セットに含まれていない場合は、fork(2) または exec(1) を呼び出す前に、親プロセスの継承可能セットをクリアして渡したい特権によって初期化します。
この例は、継承可能特権セットをクリアします。PRIV_SET パラメータは、継承可能特権セットをクリアします。ゼロ (0) パラメータは、特権 ID のパラメータリストが存在しないことを示します。
if (set_inheritable_priv(PRIV_SET, 0) == -1) perror("Cannot clear inheritable privileges");
この呼び出し以前は、継承可能セットに次の特権が含まれます。
Inheritable = file_mac_write&file_setpriv$proc_setid
呼び出し後は、継承可能セットに次の特権が含まれます。
Inheritable = none
次の例は、継承可能特権セット内に proc_setid 特権を設定します。継承可能セットには、許可セット内のどの特権でも含めることができますが、このセットにそれ以外の特権を含めると Invalid Argument エラーが発生します。proc_setid 特権は、executable の許可セットに存在するため、継承可能セットに含めることができます。この特権は execfile の許容セットにも存在するため、「ファイルの実行」において、execfile が exec に使用されるときに新しいプログラムで使用できます。
if (set_inheritable_priv(PRIV_ON, 1, PRIV_PROC_SETID) == -1) perror("Cannot set proc_setid privilege in inheritable set");
このコールの後、継承可能セットは次の特権を含みます。
Inheritable = proc_setid
子プロセスがフォークによって作成される場合、そのプロセス特権セットは親のプロセス特権セットと同じです。これは、プロセス特権セットの照会、プロセスのフォーク、および子プロセスの特権セットの照会によって確認できます。
fork の実行前は、親プロセスには次の特権が含まれます。
Forked Inheritable = proc_setid
Forked Saved = file_setpriv$proc_setid
Forked Permitted = file_setpriv$proc_setid
Forked Effective = none
pid = fork(); if (pid > 0) exit(0); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_INHERITABLE, &priv_get); printf("retval = %d errno = %d¥n", retval, errno); priv_set_to_str(&priv_get, '$', buffer, &length); printf("Forked Inheritable = %s¥n", buffer); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_SAVED, &priv_get); printf("retval = %d errno = %d¥n", retval, errno); priv_set_to_str(&priv_get, '$', buffer, &length); printf("Forked Saved = %s¥n", buffer); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_PERMITTED, &priv_get); printf("retval = %d errno = %d¥n", retval, errno); priv_set_to_str(&priv_get, '$', buffer, &length); printf("Forked Permitted = %s¥n", buffer); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_EFFECTIVE, &priv_get); printf("retval = %d errno = %d¥n", retval, errno); priv_set_to_str(&priv_get, '$', buffer, &length); printf("Forked Effective = %s¥n", buffer);
fork(2) システムコールの後、printf 文によって次のように出力されます。
Forked Inheritable = proc_setid
Forked Saved = file_setpriv$proc_setid
Forked Permitted = file_setpriv$proc_setid
Forked Effective = none
ファイルが exec により使用されると、「プロセス特権セット」で説明しているアルゴリズムに基づいて、プロセス特権セットが計算されます。
新しいプログラムの execfile は、次のファイル特権セットを含みます。これらは、exec を実行するプロセスのアプリケーションコードによって設定されたものです。
execfile Allowed = file_mac_write$proc_setid
execfile Forced = file_mac_write
exec を実行するプロセスには、次のプロセス特権セットが含まれます。
Exec'd Inheritable = proc_setid
Exec'd Saved = file_setpriv$proc_setid
Exec'd Permitted = file_setpriv$proc_setid
Exec'd Effective = none
retval = execv(execfile, argv);
exec(1) システムコールの後、プロセス特権セットは次のようになります。
execfile Allowed = file_mac_write$proc_setid
execfile Forced = file_mac_write
Exec'd Inheritable = proc_setid
Exec'd Saved = proc_setid
Exec'd Permitted = file_mac_write$proc_setid
Exec'd Effective = file_mac_write$proc_setid
exec されたプログラムの有効特権は、デフォルトではオンです。新しいプログラムは有効セット内に proc_setid 特権を含むため、ユーザー ID が変わるときに有効セットと保存セットがどのように変化するかを setuid(2) を呼び出して確認できます。詳細は、「ユーザー ID の変更」を参照してください。
retval = setuid(0); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_EFFECTIVE, &priv_get); priv_set_to_str(&priv_get, '$', buffer, &length); printf("Executable setuid effective = %s¥n", buffer); PRIV_EMPTY(&priv_get); retval = getppriv(PRIV_SAVED, &priv_get); priv_set_to_str(&priv_get, '$', buffer, &length); printf("Executable setuid saved = %s¥n", buffer);
printf 文によって、次のように出力されます。
Executable setuid effective = none
Executable setuid saved = file_mac_write$proc_setid