ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
Oracle Solaris セキュリティーサービス開発ガイド Oracle Solaris 10 8/11 Information Library (日本語) |
1. Oracle Solaris の開発者向けセキュリティー機能 (概要)
3. PAM アプリケーションおよび PAM サービスの記述
8. Oracle Solaris 暗号化フレームワークの紹介
ここでは、GSS-API を使用してセキュリティー保護されたデータ交換処理を実装する方法について説明します。ただし、GSS-API を使用するうえでもっとも中心となる関数に焦点を当てます。詳細は、付録 B GSS-API リファレンスを参照してください。この付録には、すべての GSS-API 関数、状態コード、およびデータ型の一覧が含まれています。各 GSS-API 関数についての詳細は、個々のマニュアルページを参照してください。
このマニュアルの例では単純なモデルを使用します。クライアントアプリケーションがリモートサーバーに直接データを送信します。RPC などのトランスポートプロトコル層による仲介は発生しません。
GSS-API を使用する際の一般的な手順は、次のとおりです。
各アプリケーション (送信側と受信側の両方) は資格を明示的に獲得します (資格を自動的に獲得していない場合)。
送信側はセキュリティーコンテキストを起動します。受信側はそのコンテキストを受け入れます。
送信側は転送するデータにセキュリティー保護を適用します。送信側はメッセージを暗号化するか、データに識別タグを付けます。その後、送信側は保護されたメッセージを転送します。
注 - 送信側はセキュリティー保護を適用しなくてもかまいません。その場合、デフォルトの GSS-API セキュリティーサービスである認証だけがメッセージに適用されます。
受信側はメッセージを復号化し (必要であれば)、メッセージを検証します (該当する場合)。
(省略可能) 確認のため、受信側は識別タグを送信側に返送します。
送信側と受信側のアプリケーションは両方とも共有セキュリティーコンテキストを無効にします。必要であれば、アプリケーションは残りの GSS-API データもすべて解放します。
注意 - 割り当てられたすべてのデータ領域を解放することは、呼び出し元のアプリケーションの責任です。 |
GSS-API を使用するアプリケーションは、ファイル gssapi.h をインクルード (include) する必要があります。
「資格」とは、主体名に対するアプリケーションの要求の証明を提供するデータ構造です。アプリケーションは、資格を使って自身の大域アイデンティティーを確立します。さらに資格は、エンティティーの権限を確認する目的で使用される場合もあります。
GSS-API 自身は資格を提供しません。資格は、GSS-API 関数が呼び出される前に、GSS-API の背後にあるセキュリティー機構によって作成されます。多くの場合、ユーザーはログイン時に資格を受け取ります。
ある特定の GSS-API 資格は単一の主体に対してのみ有効です。単一の資格には、その主体に対する要素が複数個含まれる可能性があります。それらの要素は機構ごとに 1 つずつ作成されます。複数のセキュリティー機構を備えたマシン上で獲得した資格は、それらの機構のサブセットを備えたマシンに転送された場合に有効になります。GSS-API は gss_cred_id_t 構造体を通じて資格にアクセスします。この構造体のことを「資格ハンドル」と呼びます。資格はアプリケーションに対して不透明です。したがって、アプリケーションは与えられた資格の詳細を知る必要はありません。
資格には 3 つの形式があります。
セキュリティーコンテキストが確立できるようになるまでに、サーバーとクライアントはそれぞれの資格を獲得する必要があります。資格は有効期限が切れるまで何度でも使用できます。有効期限が切れると、アプリケーションは資格を獲得し直す必要があります。クライアントが使用する資格とサーバーが使用する資格とでは、その有効期間が異なる場合があります。
GSS-API ベースのアプリケーションが資格を獲得する方法には、次の 2 つがあります。
ほとんどの場合、gss_acquire_cred() を呼び出すのは、コンテキストの受け入れ側、つまりサーバーだけです。コンテキストの起動側、つまりクライアントは一般に、ログイン時に資格を受け取ります。したがって、クライアントは通常、デフォルトの資格を指定できます。サーバーは、gss_acquire_cred() を使用せずに、自身のデフォルトの資格を使用することもできます。
クライアントの資格は、そのクライアントの身元をほかのプロセスに対して証明します。サーバーが資格を獲得すると、セキュリティーコンテキストの受け入れが可能になります。したがって、クライアントがサーバーに ftp 要求を送る場合、そのクライアントはログイン時からすでに資格を獲得している可能性があります。クライアントがコンテキストを起動しようとすると、GSS-API は自動的にその資格を取得します。しかし、サーバープログラムは要求されたサービス (ftp) の資格を明示的に獲得します。
gss_acquire_cred() が正常終了すると、GSS_S_COMPLETE が返されます。有効な資格を返せない場合は、GSS_S_NO_CRED が返されます。その他のエラーコードについては、gss_acquire_cred(3GSS) のマニュアルページを参照してください。具体例については、「資格の獲得」 in 第 6 章GSS-API サーバー例を参照してください。
gss_add_cred() は gss_acquire_cred() に似ています。しかし、gss_add_cred() を使用すれば、アプリケーションは既存の資格を基に新しいハンドルを作成したり、既存の資格に新しい資格要素を追加したりできます。GSS_C_NO_CREDENTIAL を既存の資格として指定した場合、gss_add_cred() はデフォルトの動作に従って新しい資格を作成します。詳細は、gss_add_cred(3GSS) のマニュアルページを参照してください。
GSS-API がセキュリティー提供時に行うもっとも重要な作業は、セキュリティーコンテキストを作成することと、データを保護することの 2 つです。アプリケーションは必要な資格を獲得したあと、セキュリティーコンテキストを確立する必要があります。コンテキストを確立するには、一方のアプリケーション (通常はクライアント) がコンテキストを起動し、もう一方のアプリケーション (通常はサーバー) がそのコンテキストを受け入れます。ピア間で複数のコンテキストが存在してもかまいません。
通信中のアプリケーションは、認証トークンを交換することによって、結合セキュリティーコンテキストを確立します。セキュリティーコンテキストは、2 つのアプリケーション間で共有すべき情報が入っている一対の GSS-API データ構造体です。この情報は、各アプリケーションのセキュリティーにおける状態を記述します。セキュリティーコンテキストはデータの保護のために必要です。
アプリケーションとリモートピア間でセキュリティーコンテキストを起動するには、gss_init_sec_context() 関数が使用されます。処理が成功すると、この関数は、確立すべきコンテキストの「コンテキストハンドル」と、受け入れ側に送信すべきコンテキストレベルトークンを返します。gss_init_sec_context() を呼び出す前に、クライアントは次の作業を行う必要があります。
必要であれば、gss_acquire_cred() で資格を獲得します。通常の場合、クライアントはログイン時に資格を受け取ります。gss_acquire_cred() は単純に、実行中のオペレーティングシステムから初期の資格を取得できます。
gss_import_name() で、サーバー名を GSS-API 内部形式にインポートします。名前と gss_import_name() についての詳細は、「GSS-API における名前」を参照してください。
gss_init_sec_context() を呼び出す際、クライアントは通常、次の引数値を渡します。
GSS_C_NO_CREDENTIAL を引数 cred_handle に渡して、デフォルトの資格を示します
GSS_C_NULL_OID を引数 mech_type に渡して、デフォルトの機構を示します
GSS_C_NO_CONTEXT を引数 context_handle に渡して、初期コンテキストが空であることを示します。gss_init_sec_context() は通常ループ内で呼び出されるため、後続の呼び出しは以前の呼び出しで戻されたコンテキストハンドルを渡す必要があります
GSS_C_NO_BUFFER を引数 input_token に渡して、トークンが最初は空であることを示します。あるいは、アプリケーションは length フィールドが 0 に設定されている gss_buffer_desc オブジェクトへのポインタを渡すこともできます
gss_import_name() で GSS-API 内部形式にインポートされたサーバー名を渡します
アプリケーションは必ずしもこのようなデフォルト値を使用する必要はありません。さらに、クライアントは引数 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(3GSS) のマニュアルページを参照してください。
gss_init_sec_context() が正常終了すると、GSS_S_COMPLETE が返されます。コンテキスト確立トークンがピアとなるアプリケーションから要求された場合、GSS_S_CONTINUE_NEEDED が返されます。エラーが発生した場合、gss_init_sec_context(3GSS) のマニュアルページに記載されたエラーコードが返されます。
コンテキストの起動が失敗した場合、クライアントはサーバーから切断されます。
コンテキストの確立におけるもう 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() を呼び出すとき、サーバーは次の引数を設定できます。
cred_handle – gss_acquire_cred() によって返された資格ハンドル。あるいは、デフォルトの資格を示す GSS_C_NO_CREDENTIAL も使用できます。
context_handle – GSS_C_NO_CONTEXT は初期コンテキストが空であることを示します。gss_init_sec_context() は通常ループ内で呼び出されるため、後続の呼び出しは以前の呼び出しで戻されたコンテキストハンドルを渡す必要があります。
input_token – クライアントから受け取ったコンテキストトークン。
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(3GSS) のマニュアルページにも例が記載されています。
繰り返しになりますが、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_init_sec_context() 関数では、アプリケーションは、基本的なコンテキストの確立以外に、追加のデータ保護サービスを要求できるようになっています。このようなサービスを要求するには、gss_init_sec_context() の req_flags 引数を使用します。
すべての機構がこれらすべてのサービスを提供するわけではありません。gss_init_sec_context() の ret_flags 引数は、指定されたコンテキストでどのサービスが利用できるかを示します。同様に、コンテキスト受け入れ側では、gss_accept_sec_context() が返す ret_flags 値から、どのサービスを利用できるかを判断します。以降の節では、追加のサービスについて説明します。
許可されていれば、コンテキスト起動側はコンテキスト受け入れ側が代理として動作するように要求できます。そのような場合、受け入れ側は、起動側に代わって別のコンテキストを起動できます。
マシン 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 つのプロセスでしか保持できない、という点です。
ftp サイトにファイルを転送するユーザーは通常、そのサイトの身元についての証明を必要としません。これに対し、アプリケーションからクレジットカード番号の提供を求められたユーザーは、その受信側の身元についての確実な証明を得たいはずです。そうした場合に「相互認証」が必要になります。コンテキストの起動側と受け入れ側の両方が、自身の身元を証明する必要があります。
コンテキスト起動側が相互認証を要求するには、gss_init_sec_context() の req_flags 引数に値 GSS_C_MUTUAL_FLAG を設定します。相互認証が承認されると、この関数はそのことを示すために、ret_flags 引数にこの値を設定します。相互認証が要求されたが使用できない場合、適切な対処を行うのは起動側のアプリケーションの責任です。相互認証が要求されたが使用できない場合でも、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_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_addrtype を GSS_C_AF_INET に設定した場合、initiator_address がインターネットアドレス形式 (つまり IP アドレス) であることを示します。同様に、3 番目と 4 番目のフィールドは受け入れ側のアドレスとアドレス型を示します。最後のフィールド application_data は、アプリケーションが自由に使用することができます。application_data を使用する予定がない場合、このフィールドを GSS_C_NO_BUFFER に設定します。アプリケーションがアドレスを指定しない場合、アドレス型フィールドを GSS_C_AF_NULLADDR に設定します。有効なアドレス型の値については、「チャネルバインディングのアドレス型」を参照してください。
アドレス型は、特定のアドレス形式を示すのではなく、アドレスファミリを示します。アドレスファミリが複数の代替アドレス形式を持つ場合、どの形式を使用しているかを判断できるだけの十分な情報を、initiator_address と acceptor_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_FLAG と GSS_C_PROT_READY_FLAG が機密性を示します。ret_flags については、「GSS-API におけるコンテキストの起動」または 「GSS-API におけるコンテキストの受け入れ」を参照してください。
機構はそれぞれ、チャネルバインディングにおけるアドレスとアドレス型に追加の制限を課すことができます。たとえば、機構は、チャネルバインディングの initiator_address フィールドが gss_init_sec_context() に返されるかどうかを検証したりできます。したがって、アプリケーションの移植性を高めるには、アドレスフィールドに正しい情報を設定する必要があります。正しい情報を決定できない場合は、GSS_C_AF_NULLADDR をアドレス型に指定する必要があります。
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_inquire_context(3GSS) を提供します。コンテキストは完全でなくてもかまわない点に注意してください。コンテキストハンドルを指定すると、gss_inquire_context() はそのコンテキストについて次の情報を提供します。
コンテキスト起動側の名前
コンテキスト受け入れ側の名前
コンテキストが有効である時間 (秒)
コンテキストで使用されるセキュリティー機構
いくつかのコンテキストパラメータフラグ。これらのフラグは gss_accept_sec_context(3GSS) 関数の ret_flags 引数と同じです。これらのフラグは、委託や相互認証などを請け負います。「GSS-API におけるコンテキストの受け入れ」を参照してください。
照会元のアプリケーションがコンテキスト起動側であるかどうかを示すフラグ
コンテキストが完全に確立されているかどうかを示すフラグ
2 つのピア間でコンテキストが確立されたあと、メッセージを送信する前にそのメッセージを保護できます。
コンテキストの確立時に使用されるのは、もっとも基本的な GSS-API 保護である 「認証」だけです。実際のセキュリティー機構によって異なりますが、GSS-API は次の 2 つの保護も提供します。
整合性 – gss_get_mic() 関数によってメッセージに対するメッセージ整合性コード (MIC) が生成されます。受信側は、MIC を検査することで、受信したメッセージが送信されたメッセージと同じかどうかを確認できます。
機密性 – MIC の使用に加え、メッセージが暗号化されます。暗号化を実行するのは、GSS-API の gss_wrap() 関数です。
gss_get_mic() と gss_wrap() の違いを、次の図に示します。gss_get_mic() を使用した場合、受信側はメッセージが変更されていないことを示すタグを受け取ります。gss_wrap() を使用した場合、受信側は暗号化されたメッセージとタグを受け取ります。
図 4-7 gss_get_mic() と gss_wrap()
使用すべき関数はユーザーの状況に応じて異なります。gss_wrap() は整合性サービスも含むため、多くのプログラムは gss_wrap() を使用します。プログラムは、機密性サービスが利用可能かどうかを判定できます。続いてプログラムは、その利用可能性に応じて、機密性を指定して、あるいは指定しないで、gss_wrap()を呼び出すことができます。使用例については、「メッセージのラップと送信」を参照してください。ただし、gss_get_mic() を使用するとメッセージをラップ解除する必要がないため、gss_wrap() を使用する場合よりも CPU サイクルを節約できます。したがって、機密性が必要ないプログラムは 、gss_get_mic() でメッセージを保護する可能性があります。
gss_get_mic() を使用すると、プログラムは暗号化 MIC をメッセージに追加できます。受信側は、gss_verify_mic() でメッセージの MIC を検査できます。
gss_get_mic() は gss_wrap() とは対照的に、メッセージと MIC を別々に出力します。この分離は、送信側アプリケーションがメッセージと対応する MIC の両方を送信する必要があることを意味します。さらに重要なのは、受信側がメッセージと MIC を区別できる必要がある、という点です。メッセージと MIC を適切に処理するには、次のいずれかの方法を使用します。
プログラム制御 (つまり、状態) を通じて。受信側アプリケーションは受信関数を 2 回呼び出す (つまり、1 回目はメッセージを取得するため、2 回目はメッセージの MIC を取得するため) ことをあらかじめ知ることができます。
フラグを通じて。送信側と受信側は、どの種類のトークンを含めるかをフラグで示すことができます。
メッセージと MIC の両方を含むユーザー定義トークン構造体を通じて。
gss_get_mic() が正常終了すると、GSS_S_COMPLETE が返されます。指定された QOP が有効でない場合、GSS_S_BAD_QOP が返されます。詳細は、gss_get_mic(3GSS) を参照してください。
メッセージは、gss_wrap() 関数でラップすることが可能です。gss_get_mic() と同様に、gss_wrap() は MIC を提供します。また、機密性が要求され、かつ実際の機構で利用可能である場合には、gss_wrap() は指定されたメッセージの暗号化も行います。メッセージの受信側は gss_unwrap() でメッセージをラップ解除します。
gss_wrap() は gss_get_mic() とは違い、メッセージと MIC をいっしょにラップし、1 つの出力メッセージにします。このバンドルを送信する関数の呼び出しは、1 回だけですみます。これに対し、gss_unwrap() はメッセージを抽出します。MIC はアプリケーションからは見えません。
メッセージが正常にラップされた場合、gss_wrap() は GSS_S_COMPLETE を戻します。要求された QOP が有効でない場合、GSS_S_BAD_QOP が返されます。gss_wrap() の使用例については、「メッセージのラップと送信」を参照してください。
gss_wrap() でメッセージをラップすると、送信すべきデータのサイズが増加します。保護されたメッセージパケットは、指定された転送プロトコルを通過するのに適したサイズである必要があります。したがって、GSS-API は関数 gss_wrap_size_limit() を提供しています。 gss_wrap_size_limit() は、プロトコルにとって大きすぎないサイズにラップ可能なメッセージの最大サイズを計算します。この最大サイズを超える場合、アプリケーションは gss_wrap() を呼び出す前にメッセージを分割できます。メッセージを実際にラップする前にラップサイズの制限値を必ず検査してください。
変形を行うためにどの QOP アルゴリズムを使用するか
機密性を呼び出すかどうか
デフォルトの QOP は、GSS-API の実装ごとに異なる可能性があります。したがって、デフォルトの QOP を指定した場合でも、ラップ後のメッセージのサイズが異なる可能性があります。この可能性を示したのが次の図です。
機密性を適用するかどうかにかかわらず、gss_wrap() によってメッセージのサイズが増加します。gss_wrap() は、転送メッセージ内に MIC を埋め込みます。しかし、メッセージを暗号化すると (機密性を適用すると)、メッセージのサイズはさらに増加します。このプロセスを示したのが次の図です。
gss_wrap_size_limit() が正常終了すると、GSS_S_COMPLETE が返されます。指定された QOP が有効でない場合、GSS_S_BAD_QOP が返されます。gss_wrap_size_limit() で元のメッセージの最大サイズを求める例については、「メッセージのラップと送信」を参照してください。
この呼び出しが正常に終了したとしても、gss_wrap() が max-input-size バイトの長さを持つメッセージを必ず保護できるという保証はありません。この機能は、gss_wrap() の呼び出し時点で必要なシステムリソースが利用可能かどうかに依存します。詳細は、gss_wrap_size_limit(3GSS) のマニュアルページを参照してください。
コンテキスト起動側がコンテキスト受け入れ側に一連のデータパケットを順次転送する際、一部の機構では、その順序が正しいかどうかをコンテキスト受け入れ側が検査できるようになっています。これらの検査には、「パケットが正しい順序で到着したか」、「パケットが不必要に重複していないか」が含まれます。次の図を参照してください。受け入れ側がこれら 2 つの状態を検査するのは、パケットの検証時とパケットのラップ解除時です。詳細は、「メッセージのラップ解除」を参照してください。
図 4-8 リプレイされたメッセージと順序が正しくないメッセージ
起動側は gss_init_sec_context() で順序を検査できます。それには、GSS_C_REPLAY_FLAG または GSS_C_SEQUENCE_FLAG を論理 OR で req_flags 引数に設定します。
受信側は、転送メッセージのラップ解除後または検証後に、確認を送信側に返すことができます。つまり、そのメッセージの MIC を返送します。送信側がラップはしなかったが gss_get_mic() で MIC をタグ付けしているメッセージの場合を考えます。実行手順 (図 4-9) は次のようになります。
起動側は gss_get_mic() でメッセージにタグ付けします。
起動側はメッセージと MIC を受け入れ側に送信します。
受け入れ側は gss_verify_mic() でメッセージを検証します。
受け入れ側は MIC を起動側に返送します。
起動側は gss_verify_mic() で、元のメッセージに対して受信した MIC を検証します。
図 4-9 MIC データの確認
ラップされたデータの場合、gss_unwrap() 関数はメッセージと MIC を別々に生成しません。したがって、受信側は、受信した (およびラップ解除した) メッセージから MIC を生成する必要があります。実行手順 (図 4-10) は次のようになります。
起動側は gss_wrap() でメッセージをラップします。
起動側はラップしたメッセージを送信します。
受け入れ側は gss_unwrap() でメッセージをラップ解除します。
受け入れ側は gss_get_mic() でラップ解除されたメッセージの MIC を生成します。
受け入れ側は抽出した MIC を起動側に返信します。
起動側は gss_verify_mic() で、元のメッセージに対して受信した MIC を検証します。
アプリケーションは、GSS-API データ用に割り当てられたすべてのデータ領域を解放します。これに関係する関数は、gss_release_buffer(3GSS)、gss_release_cred(3GSS)、gss_release_name(3GSS)、および gss_release_oid_set(3GSS) です。
図 4-10 ラップされたデータの確認
最終的に、すべてのメッセージの送受信が完了し、起動側と受け入れ側のアプリケーションが終了します。この時点で、両アプリケーションは gss_delete_sec_context() を呼び出して共有コンテキストを破棄します。gss_delete_sec_context() はコンテキストに関連するローカルのデータ構造体を削除します。
用心のため、アプリケーションは GSS-API データ用に割り当てたデータ領域をすべて解放するべきです。このような関数には、gss_release_buffer()、gss_release_cred()、gss_release_name()、および gss_release_oid_set() があります。