Oracle Solaris セキュリティーサービス開発ガイド

GSS-API におけるコンテキストの操作

GSS-API がセキュリティー提供時に行うもっとも重要な作業は、セキュリティーコンテキストを作成することと、データを保護することの 2 つです。アプリケーションは必要な資格を獲得したあと、セキュリティーコンテキストを確立する必要があります。コンテキストを確立するには、一方のアプリケーション (通常はクライアント) がコンテキストを起動し、もう一方のアプリケーション (通常はサーバー) がそのコンテキストを受け入れます。ピア間で複数のコンテキストが存在してもかまいません。

通信中のアプリケーションは、認証トークンを交換することによって、結合セキュリティーコンテキストを確立します。セキュリティーコンテキストは、2 つのアプリケーション間で共有すべき情報が入っている一対の GSS-API データ構造体です。この情報は、各アプリケーションのセキュリティーにおける状態を記述します。セキュリティーコンテキストはデータの保護のために必要です。

GSS-API におけるコンテキストの起動

アプリケーションとリモートピア間でセキュリティーコンテキストを起動するには、gss_init_sec_context() 関数が使用されます。処理が成功すると、この関数は、確立すべきコンテキストの「コンテキストハンドル」と、受け入れ側に送信すべきコンテキストレベルトークンを返します。gss_init_sec_context() を呼び出す前に、クライアントは次の作業を行う必要があります。

  1. 必要であれば、gss_acquire_cred() で資格を獲得します。通常の場合、クライアントはログイン時に資格を受け取ります。gss_acquire_cred() は単純に、実行中のオペレーティングシステムから初期の資格を取得できます。

  2. gss_import_name() で、サーバー名を GSS-API 内部形式にインポートします。名前と gss_import_name() についての詳細は、「GSS-API における名前」を参照してください。

gss_init_sec_context() を呼び出す際、クライアントは通常、次の引数値を渡します。

アプリケーションは必ずしもこのようなデフォルト値を使用する必要はありません。さらに、クライアントは引数 req_flags を使用して、他のセキュリティーパラメータに対する要件を指定することもできます。gss_init_sec_context() の引数についての詳細は、以降の節で説明します。

コンテキスト受け入れ側は、コンテキスト確立時にいくつかのハンドシェークを要求する可能性があります。つまり、受け入れ側は、コンテキストが完全に確立されるまで、複数のコンテキスト情報を送信するように起動側に要求できます。したがって、移植性のため、コンテキストの起動は常に、コンテキストが完全に確立されたかどうかを検査するループの一部として行われる必要があります。

コンテキストが完全に確立されていない場合、gss_init_sec_context() はメジャー状態コードとして GSS_C_CONTINUE_NEEDED を戻します。したがって、その gss_init_sec_context() からの戻り値を使用して、起動ループを継続するかどうかの判定を行う必要があります。

クライアントはコンテキスト情報をサーバーに、gss_init_sec_context() から戻された「出力トークン」の形式で渡します。クライアントは、サーバーから情報を「入力トークン」として受け取ります。その後、その入力トークンは、後続の gss_init_sec_context() 呼び出しの引数として渡すことができます。受け取った入力トークンの長さが 0 の場合、サーバーはこれ以上出力トークンを要求していないことが分かります。

したがって、ループ内で gss_init_sec_context() の戻り値の状態を検査する以外に、入力トークンの長さも検査する必要があります。その長さが 0 以外の値である場合、別のトークンをサーバーに送信する必要があります。ループを開始する前に、入力トークンの長さを 0 に初期化しておく必要があります。入力トークンを GSS_C_NO_BUFFER に設定するか、その構造体の length フィールドの値を 0 に設定します。

次の擬似コードは、クライアント側からのコンテキストの確立例を示したものです。

コンテキストを GSS_C_NO_CONTEXT で初期化する
入力トークンを GSS_C_NO_BUFFER で初期化する

do

     call gss_init_sec_context(資格, コンテキスト, 名前, 入力トークン, 
                                           出力トークン, その他の引数...)

     if (受け入れ側に送信すべき出力トークンが存在する)
          受け入れ側に出力トークンを送信する
          出力トークンを解放する

     if (コンテキストが完全でない)
          受け入れ側から入力トークンを受信する

     if (GSS-API エラーが発生した)
          コンテキストを削除する

until コンテキストが完成

実際のループは、さまざまなエラー検査を含んだ、より複雑なものになります。そうしたコンテキスト起動ループの実際の例については、「サーバーとのセキュリティーコンテキストの確立」を参照してください。さらに、gss_init_sec_context(3GSS) のマニュアルページにも、上記例ほど一般化されていない例があります。

一般に、コンテキストが完全に確立されていない時に戻されるパラメータ値は、コンテキストが完了した時に戻されるはずの値です。詳細は、gss_init_sec_context() のマニュアルページを参照してください。

gss_init_sec_context() が正常終了すると、GSS_S_COMPLETE が返されます。コンテキスト確立トークンがピアとなるアプリケーションから要求された場合、GSS_S_CONTINUE_NEEDED が返されます。エラーが発生した場合、gss_init_sec_context(3GSS) のマニュアルページに記載されたエラーコードが返されます。

コンテキストの起動が失敗した場合、クライアントはサーバーから切断する必要があります。

GSS-API におけるコンテキストの受け入れ

コンテキストの確立におけるもう 1 つの仕事は、コンテキストの受け入れです。コンテキストの受け入れは gss_accept_sec_context() 関数で行います。通常の場合、クライアントが gss_init_sec_context() で起動したコンテキストを、サーバーが受け入れます。

gss_accept_sec_context() への主な入力は、起動側から受け取った入力トークンです。gss_accept_sec_context は、コンテキストハンドルと起動側に戻すべき出力トークンを戻します。しかし、gss_accept_sec_context() を呼び出す前に、サーバーはクライアントから要求されたサービスの資格を獲得しておく必要があります。サーバーはこのような資格を gss_acquire_cred() 関数で獲得します。あるいは、サーバーは、資格を明示的に獲得するのではなく、gss_accept_sec_context() を呼び出す際にデフォルトの資格 (GSS_C_NO_CREDENTIAL) を指定することもできます。

gss_accept_sec_context() を呼び出すとき、サーバーは次の引数を設定できます。

gss_accept_sec_context() 引数についての詳細は、以降の節で説明します。

セキュリティーコンテキストを確立するためには、いくつかのハンドシェークが必要となる可能性があります。コンテキストが完全に確立されるまでに、起動側と受け入れ側は通常、複数のコンテキスト情報を送信する必要があります。したがって、移植性のため、コンテキストの受け入れは常に、コンテキストが完全に確立されたかどうかを検査するループの一部として行われる必要があります。コンテキストがまだ確立されていない場合、gss_accept_sec_context() はメジャー状態コード GSS_C_CONTINUE_NEEDED を返します。したがって、ループは gss_accept_sec_context() の戻り値を使用して、受け入れループを継続するかどうかを判定する必要があります。

コンテキスト受け入れ側はコンテキスト情報をコンテキスト起動側に、gss_accept_sec_context() から戻された出力トークンの形式で渡します。その後、受け入れ側は、起動側から追加情報を入力トークンとして受け取れます。入力トークンは、後続の gss_accept_sec_context() 呼び出しの引数として渡されます。起動側に送信すべきトークンがなくなると、gss_accept_sec_context() から長さ 0 の出力トークンが返されます。ループ内で、gss_accept_sec_context() の戻り値の状態を検査する以外に、出力トークンの長さを検査して、別のトークンを送信すべきかどうかを判断する必要があります。ループを開始する前に、出力トークンの長さを 0 に初期化しておく必要があります。出力トークンを GSS_C_NO_BUFFER に設定するか、その構造体の length フィールドの値を 0 に設定します。

次の擬似コードは、サーバー側からのコンテキストの確立例を示したものです。

コンテキストを GSS_C_NO_CONTEXT で初期化する
出力トークンを GSS_C_NO_BUFFER で初期化する

do

     起動側から入力トークンを受信する

     call gss_accept_sec_context(コンテキスト, 資格ハンドル, 入力トークン,
                                                出力トークン, その他の引数...)

     if (起動側に送信すべき出力トークンが存在する)
          起動側に出力トークンを送信する
          出力トークンを解放する

     if (GSS-API エラーが発生した)
          コンテキストを削除する

until コンテキストが完成

実際のループは、さまざまなエラー検査を含んだ、より複雑なものになります。そうしたコンテキスト受け入れループの実際の例については、「サーバーとのセキュリティーコンテキストの確立」を参照してください。さらに、gss_accept_sec_context() のマニュアルページにも例が記載されています。

繰り返しになりますが、GSS-API 自身はトークンを送受信しません。トークンの送受信はアプリケーションが処理する必要があります。トークンを転送する関数の例については、「その他の GSS-API 関数例」を参照してください。

正常に終了した場合、gss_accept_sec_context()GSS_S_COMPLETE を戻します。コンテキストが完全に確立されていない場合、gss_accept_sec_context は GSS_S_CONTINUE_NEEDED を戻します。エラーが発生した場合、この関数はエラーコードを戻します。詳細は、gss_accept_sec_context(3GSS) のマニュアルページを参照してください。

GSS-API におけるその他のコンテキストサービスの使用

gss_init_sec_context() 関数では、アプリケーションは、基本的なコンテキストの確立以外に、追加のデータ保護サービスを要求できるようになっています。このようなサービスを要求するには、gss_init_sec_context()req_flags 引数を使用します。

すべての機構がこれらすべてのサービスを提供するわけではありません。gss_init_sec_context()ret_flags 引数は、指定されたコンテキストでどのサービスが利用できるかを示します。同様に、コンテキスト受け入れ側では、gss_accept_sec_context() が返す ret_flags 値から、どのサービスを利用できるかを判断します。以降の節では、追加のサービスについて説明します。

GSS-API における資格の委託

許可されていれば、コンテキスト起動側はコンテキスト受け入れ側が代理として動作するように要求できます。そのような場合、受け入れ側は、起動側に代わって別のコンテキストを起動できます。

マシン A 上のあるユーザーが、マシン B に rlogin したあと、さらにマシン B からマシン C に rlogin したいものとします。このとき、委託された資格は B を A として識別するか、あるいは A のプロキシとして動作している B として識別するかは、機構によって異なります。

委託が許可されると、ret_flags に値 GSS_C_DELEG_FLAG を設定できます。受け入れ側は委託された資格を gss_accept_sec_context()delegated_cred_handle 引数として受け取ります。資格の委託はコンテキストのエクスポートとは異なります。「GSS-API におけるコンテキストのエクスポートとインポート」を参照してください。その違いの 1 つは、アプリケーションの資格は一度に複数回委託できますが、コンテキストは一度に 1 つのプロセスでしか保持できない、という点です。

GSS-API におけるピア間での相互認証の実行

ftp サイトにファイルを転送するユーザーは通常、そのサイトの身元についての証明を必要としません。これに対し、アプリケーションからクレジットカード番号の提供を求められたユーザーは、その受信側の身元についての確実な証明を得たいはずです。そうした場合に「相互認証」が必要になります。コンテキストの起動側と受け入れ側の両方が、自身の身元を証明する必要があります。

コンテキスト起動側が相互認証を要求するには、gss_init_sec_context()req_flags 引数に値 GSS_C_MUTUAL_FLAG を設定します。相互認証が承認されると、この関数はそのことを示すために、ret_flags 引数にこの値を設定します。相互認証が要求されたが使用できない場合、適切な対処を行うのは起動側のアプリケーションの責任です。相互認証が要求されたが使用できない場合でも、GSS-API は自動的にコンテキストを終了しません。また、機構の中には、特に要求がなくても相互認証を常に実行するものもあります。

GSS-API における匿名認証の実行

GSS-API の通常の使用においては、起動側の識別情報はコンテキスト確立の一部として、受け入れ側で使用できるようになります。しかし、コンテキスト起動側は自身の識別情報をコンテキスト受け入れ側に知らせないように要求することもできます。

たとえば、医療データベースへの無制限のアクセスを提供するアプリケーションを考えます。そのようなサービスのクライアントは、サービスの認証を要求することが考えられます。この方法では、データベースから取り出されるすべての情報に対して信頼が確立されます。しかし、クライアントはプライバシー上の理由などにより、識別情報を公開したくないかもしれません。

匿名性を要求するには、gss_init_sec_context()req_flags 引数に GSS_C_ANON_FLAG を設定します。匿名性が利用可能かどうかを検査するには、gss_init_sec_context () または gss_accept_sec_context()ret_flags 引数に GSS_C_ANON_FLAG が返されるかどうかを確認します。

匿名性が有効である場合、gss_accept_sec_context() または gss_inquire_context() から返されたクライアント名を指定して gss_display_name()を呼び出すと、汎用的な匿名が生成されます。


注 –

匿名性が要求されたが使用できない場合、適切な対処を行うのはアプリケーションの責任です。GSS-API はそのような場合にコンテキストを終了しません。


GSS-API におけるチャネルバインディングの使用

多くのアプリケーションでは、基本的なコンテキスト確立を行うだけで、コンテキスト起動側を適切に認証できます。追加のセキュリティーが必要な場合、GSS-API ではチャネルバインディングを使用します。チャネルバインディングとは、使用されている特定のデータチャネルを識別するためのタグのことです。具体的には、チャネルバインディングはコンテキストの起点と終点 (つまり起動側と受け入れ側) を識別します。これらのタグは起動側と受け入れ側のアプリケーションに固有であるため、識別情報のより有効な証明となります。

チャネルバインディングは、次に示すように、gss_channel_bindings_struct 構造体へのポインタである gss_channel_bindings_t データ型によって示されます。

typedef struct gss_channel_bindings_struct {
OM_uint32       initiator_addrtype;
gss_buffer_desc initiator_address;
OM_uint32       acceptor_addrtype;
gss_buffer_desc acceptor_address;
gss_buffer_desc application_data;
} *gss_channel_bindings_t;

最初の 2 つのフィールドは起動側のアドレスとアドレス型 (起動側のアドレスが送信される形式) を示します。たとえば、initiator_addrtypeGSS_C_AF_INET に設定した場合、initiator_address がインターネットアドレス形式 (つまり IP アドレス) であることを示します。同様に、3 番目と 4 番目のフィールドは受け入れ側のアドレスとアドレス型を示します。最後のフィールド application_data は、アプリケーションが自由に使用することができます。application_data を使用する予定がない場合、このフィールドを GSS_C_NO_BUFFER に設定します。アプリケーションがアドレスを指定しない場合、アドレス型フィールドを GSS_C_AF_NULLADDR に設定します。有効なアドレス型の値については、「チャネルバインディングのアドレス型」を参照してください。

アドレス型は、特定のアドレス形式を示すのではなく、アドレスファミリを示します。アドレスファミリが複数の代替アドレス形式を持つ場合、どの形式を使用しているかを判断できるだけの十分な情報を、initiator_addressacceptor_address のフィールドに指定する必要があります。特に指定しない限り、アドレスはネットワークのバイト順 (つまり、アドレスファミリにネイティブなバイト順) で指定します。

チャネルバインディングを使用してコンテキストを確立するには、割り当てられたチャネルバインディング構造体を gss_init_sec_context()input_chan_bindings 引数で指します。この構造体の各フィールドが連結されてオクテット文字列が生成され、そこから MIC が派生されます。次に、この MIC が出力トークンに添付されます。続いて、アプリケーションはそのトークンをコンテキスト受け入れ側に送信します。トークンを受け取った受け入れ側は、gss_accept_sec_context() を呼び出します。詳細は、「GSS-API におけるコンテキストの受け入れ」を参照してください。gss_accept_sec_context() は、受け取ったチャネルバインディングの MIC を計算します。gss_accept_sec_context() は、MIC が一致しない場合に GSS_C_BAD_BINDINGS を返します。

gss_accept_sec_context() からは転送されたチャネルバインディングが返されるため、受け入れ側は、それらの値に基づいてセキュリティー検査を実行できます。たとえば application_data の値をセキュアデータベースに保存しておいたコードワードと比較したりできます。


注 –

チャネルバインディング情報の機密性を提供するかどうかは、実際の機構によって異なります。したがって、アプリケーションは、機密性が保証されるまで、チャネルバインディングに機密情報を含めてはなりません。機密性が利用可能かどうかを判断するには、アプリケーション内で gss_init_sec_context() または gss_accept_sec_context() ret_flags 引数を検査します。値 GSS_C_CONF_FLAGGSS_C_PROT_READY_FLAG が機密性を示します。ret_flags については、「GSS-API におけるコンテキストの起動」または 「GSS-API におけるコンテキストの受け入れ」を参照してください。


機構はそれぞれ、チャネルバインディングにおけるアドレスとアドレス型に追加の制限を課すことができます。たとえば、機構は、チャネルバインディングの initiator_address フィールドが gss_init_sec_context() に返されるかどうかを検証したりできます。したがって、アプリケーションの移植性を高めるには、アドレスフィールドに正しい情報を設定する必要があります。正しい情報を決定できない場合は、GSS_C_AF_NULLADDR をアドレス型に指定する必要があります。

GSS-API におけるコンテキストのエクスポートとインポート

GSS-API は、コンテキストをエクスポートおよびインポートする方法を提供します。この機能を使えば、マルチプロセスアプリケーション (通常はコンテキスト受け入れ側) は、あるプロセスから別のプロセスにコンテキストを転送できます。たとえば、受け入れ側に、コンテキスト起動側からの応答を待つプロセスと、コンテキストに送信されたデータを使用するプロセスが存在する可能性があります。test_import_export_context() 関数の使用」では、これらの関数を使ってコンテキストを保存および復元する方法について説明しています。

関数 gss_export_sec_context() は、エクスポートされるコンテキストに関する情報が入ったプロセス間トークンを作成します。詳細は、「GSS-API におけるプロセス間トークン」を参照してください。gss_export_sec_context() を呼び出す前に、トークンを受信するバッファーを GSS_C_NO_BUFFER に設定する必要があります。

次に、アプリケーションはそのトークンをほかのプロセスに渡します。新しいプロセスはそのトークンを受け入れ、それを gss_import_sec_context() に渡します。多くの場合、アプリケーション間でトークンを渡すときに使用される関数が、プロセス間でトークンを渡すときにも使用されます。

セキュリティープロセスのインスタンスは一度に 1 つしか存在できません。gss_export_sec_context() はエクスポートされたコンテキストを無効にし、そのコンテキストハンドルを GSS_C_NO_CONTEXT に設定します。また、gss_export_sec_context() は、そのコンテキストに関連付けられたプロセス内のすべてのリソースも解放します。コンテキストのエクスポートを完了できない場合、gss_export_sec_context() は、既存のセキュリティーコンテキストを元のまま残し、プロセス間トークンも返しません。

すべての機構でコンテキストをエクスポートできるわけではありません。アプリケーションでは、gss_accept_sec_context() または gss_init_sec_context()ret_flags 引数をチェックして、コンテキストをエクスポートできるかどうかを判定できます。このフラグに GSS_C_TRANS_FLAG が設定されている場合、コンテキストはエクスポートできます。「GSS-API におけるコンテキストの受け入れ」「GSS-API におけるコンテキストの起動」を参照してください。

図 4–6 に、マルチプロセスの受け入れ側がコンテキストをエクスポートしてマルチタスクを実現している様子を示します。この例では、プロセス 1 はトークンを受け取って処理します。このステップにより、コンテキストレベルトークンとデータトークンが分離され、それらのトークンがプロセス 2 に渡されます。プロセス 2 はアプリケーション固有の方法でデータを処理します。この図では、クライアントはすでに gss_init_sec_context() からエクスポートトークンを取得しています。クライアントはトークンをユーザー定義関数 send_a_token() に渡します。send_a_token は、転送するトークンがコンテキストレベルトークンまたはメッセージトークンのどちらであるかを示します。send_a_token() はトークンをサーバーに転送します。この図には示されていませんが、おそらく、send_a_token() はスレッド間でトークンを渡すときにも使用されます。

図 4–6 コンテキストのエクスポート: マルチスレッド化された受け入れ側の例

マルチプロセスの受け入れ側が、コンテキストレベルトークンとデータトークンを分離し、次のプロセスへ各トークンを渡す方法を示しています。

GSS-API におけるコンテキスト情報の取得

GSS-API は、指定されたセキュリティーコンテキストについての情報を取得する関数 gss_inquire_context(3GSS) を提供します。コンテキストは完全でなくてもかまわない点に注意してください。コンテキストハンドルを指定すると、gss_inquire_context() はそのコンテキストについて次の情報を提供します。