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

印刷ビューの終了

更新: 2016 年 11 月
 
 

GSS-API の重要な要素

GSS-API データ型

以降のセクションでは、主な GSS-API データ型について説明します。すべての GSS-API データ型を確認するには、GSS-API データ型と値を参照してください。

GSS-API の整数

int のサイズはプラットフォームによって異なるため、GSS-API は次の整数型を提供します。OM_uint32。これは、32 ビットの符号なし整数です。

GSS-API の文字列とそれに類するデータ

GSS-API はすべてのデータを内部形式で処理するため、文字列も、GSS-API 形式に変換したあとで GSS-API 関数に渡す必要があります。GSS-API は、gss_buffer_desc 構造体を使って文字列を処理します。

typedef struct gss_buffer_desc_struct {
     size_t     length;
     void       *value;
}  gss_buffer_desc *gss_buffer_t;

gss_buffer_t は、そうした構造体へのポインタです。文字列は、それらを使用する関数に渡す前に gss_buffer_desc 構造体に変換しておく必要があります。次の例では、汎用的な GSS-API 関数を使って、送信前のメッセージに保護を適用しています。

使用例 7  GSS-API における文字列の使用法
char *message_string;
gss_buffer_desc input_msg_buffer;

input_msg_buffer.value = message_string;
input_msg_buffer.length = strlen(input_msg_buffer.value) + 1;

gss_generic_function(arg1, &input_msg_buffer, arg2...);

gss_release_buffer(input_msg_buffer);

ここで、input_msg_buffer を終了するときは、gss_release_buffer() を使って input_msg_buffer を解放する必要がある点に注意してください。

gss_buffer_desc オブジェクトは文字列だけに使用されるわけではありません。たとえば、トークンも gss_buffer_desc オブジェクトとして処理されます。詳細は、GSS-API トークンを参照してください。

GSS-API における名前

「名前」は主体を指します。ネットワークセキュリティーの用語では、「主体」とは、ユーザー、プログラム、またはシステムを指します。主体はクライアントまたはサーバーのどちらにでもなり得ます。

    主体の例を、次にいくつか挙げます。

  • 別のシステムにログインするユーザー (user@system など)

  • ネットワークサービス (nfs@system など)

  • アプリケーションを実行するシステム (mysystem@example.com など)

GSS-API では、名前は gss_name_t オブジェクトとして格納されます。このオブジェクトはアプリケーションに対して不透明です。名前を gss_buffer_t オブジェクトから gss_name_t 形式に変換するには、gss_import_name() 関数を使用します。インポートされたすべての名前には関連する名前型が割り当てられます。名前型とは、その名前の形式を示すものです。名前型については、GSS-API の OIDを参照してください。有効な名前型の一覧については、名前型を参照してください。

gss_import_name() の構文は次のとおりです。

OM_uint32 gss_import_name (
       OM_uint32          *minor-status,
       const gss_buffer_t input-name-buffer,
       const gss_OID      input-name-type,
       gss_name_t         *output-name)
minor-status

実際のメカニズムから戻されるステータスコード。GSS-API ステータスコードを参照してください。

input-name-buffer

インポートされた名前が格納される gss_buffer_desc 構造体。この構造体は、アプリケーション側で明示的に割り当てる必要があります。GSS-API の文字列とそれに類するデータ使用例 8を参照してください。この引数は、アプリケーションでの使用終了後、gss_release_buffer() を使って解放する必要があります。

input-name-type

input-name-buffer の形式を示す gss_OID。GSS-API における名前型を参照してください。また、名前型に、有効な名前型の一覧表があります。

output-name

名前を受け取る gss_name_t 構造体。

使用例 7 に示す汎用例を少し変更して、gss_import_name() の使用方法を示しています。まず、通常の文字列が gss_buffer_desc 構造体に挿入されています。次に、その文字列が、gss_import_name () によって gss_name_t 構造体内に格納されています。

使用例 8  gss_import_name()() の使用例
char *name_string;
gss_buffer_desc input_name_buffer;
gss_name_t      output_name_buffer;

input_name_buffer.value = name_string;
input_name_buffer.length = strlen(input_name_buffer.value) + 1;

gss_import_name(&minor_status, input_name_buffer, 
                    GSS_C_NT_HOSTBASED_SERVICE, &output_name);

gss_release_buffer(input_name_buffer);

インポートされた名前は、人間が読める形式で表示できるように、元の gss_buffer_t オブジェクトに返すことが可能です。それには、gss_display_name() を使用します。ただし、gss_display_name() は、結果の文字列が元と同じであることを保証しません。その原因は、実際のメカニズムが名前を格納する方法にあります。GSS-API にはほかにも名前を処理する関数があります。GSS-API 関数を参照してください。

gss_name_t 構造体には、単一の名前の複数のバージョンを格納できます。GSS-API によってサポートされているメカニズムごとに、1 つのバージョンが生成されます。つまり、user@company の gss_name_t 構造体には、Kerberos v5 から提供されたその名前の 1 つのバージョンと、別のメカニズムから提供された別のバージョンが含まれる可能性があります。関数 gss_canonicalize_name() は、入力として内部名とメカニズムを受け取ります。また、gss_canonicalize_name() は、出力としてそのメカニズムに固有の単一バージョン名だけを含む別の内部名を返します。

そうしたメカニズムに固有な名前のことをメカニズム名 (MN) と呼びます。メカニズム名とは、特定のメカニズムの名前ではなく、特定のメカニズムによって生成された主体の名前です。このプロセスを示したのが次の図です。

図 4  内部名とメカニズム名 (MN)

image:メカニズム名が派生するプロセスを示しています。

GSS-API における名前の比較

サーバーがクライアントからある名前を受け取り、その名前をアクセス制御リスト内で検索する必要がある場合を考えます。「アクセス制御リスト (Access Control List、ACL)」とは、特定のアクセス権を持つ主体のリストのことです。

    そうした検索を行うには、次のような方法が考えられます。

  1. gss_import_name() で、クライアント名を GSS-API 内部形式にインポートします (まだインポートされていない場合)。

    場合によっては、サーバーは名前を内部形式で受け取ります。その場合、この手順は必要ありません。たとえば、サーバーはクライアント自身の名前を検索する可能性があります。コンテキストの起動中、クライアント自身の名前は内部形式で渡されます。

  2. gss_import_name() で、各 ACL 名を インポートします。

  3. gss_compare_name() で、インポートした各 ACL 名をインポートしたクライアント名と比較します。

次の図に、このプロセスを示します。ここでは手順 1 が必要であると仮定します。

図 5  名前の比較 (遅い)

image:gss_compare_name 関数を使用して内部クライアント名を比較する方法を示しています。

名前の数が少ない場合は、名前を個別に比較する上記の方法でも問題ありません。名前の数が非常に多い場合は、gss_canonicalize_name() 関数を使用するほうが効率的です。

    この方法では次の手順を使用します。

  1. gss_import_name() で、クライアント名をインポートします (まだインポートされていない場合)。

    名前を比較する前述の方法と同様に、名前がすでに内部形式である場合には、この手順は必要ありません。

  2. gss_canonicalize_name() を使用してクライアント名のメカニズム名バージョンを生成します。

  3. gss_export_name() を使用してエクスポート名を生成します。エクスポート名は、連続した文字列としてのクライアント名です。

  4. memcmp() を使用してエクスポートされたクライアント名を ACL 内の個々の名前と比較します。これは、高速で動作するオーバーヘッドの少ない関数です。

次の図に、このプロセスを示します。ここでも、サーバーがクライアントから受信した名前をインポートする必要があると仮定します。

図 6  名前の比較 (速い)

image:memcmp 関数を使用して内部クライアント名を比較する方法を示しています。

gss_export_name() はメカニズム名 (MN) を期待するため、先にクライアント名に対して gss_canonicalize_name() を実行する必要があります。

詳細は、gss_export_name(3GSS)gss_import_name(3GSS)、および gss_canonicalize_name(3GSS) を参照してください。

GSS-API の OID

    オブジェクト識別子 (Object Identifier、OID) は、次のようなデータを格納するときに使用します。

  • セキュリティーメカニズム

  • QOP – Quality of Protection (保護品質) の値

  • 名前型

OID は、GSS-API の gss_OID_desc 構造体に格納されます。次の例のように、GSS-API はその構造体へのポインタ gss_OID を提供します。

使用例 9  OID の構造体
typedef struct gss_OID_desc_struct {
        OM_uint32   length;
        void        *elements;
     } gss_OID_desc, *gss_OID;

さらに、1 つ以上の OID を gss_OID_set_desc 構造体に格納することもできます。

使用例 10  OID セットの構造体
typedef struct gss_OID_set_desc_struct {
        size_t    count;
        gss_OID   elements;
     } gss_OID_set_desc, *gss_OID_set;

Caution

注意  - アプリケーションは free() で OID を解放するべきではありません。


GSS-API におけるメカニズムと QOP

GSS-API では、使用するセキュリティーメカニズムをアプリケーションが選択できるようになっていますが、GSS-API が選択したデフォルトのメカニズムをできるかぎり使用する必要があります。同様に、GSS-API では、データ保護の保護品質レベルをアプリケーションが指定できるようになっていますが、デフォルトの QOP をできる限り使用する必要があります。デフォルトのメカニズムを受け入れることを示すには、メカニズムまたは QOP を期待する関数に値 GSS_C_NULL_OID を引数として渡します。


Caution

注意  - セキュリティーメカニズムまたは QOP を明示的に指定することは、GSS-API の使用目的に反します。そうした特定の選択は、アプリケーションの移植性を制限します。ほかの GSS-API 実装は、その QOP またはメカニズムを意図した方法でサポートしていない可能性があります。ただし、OID の指定では、利用可能なメカニズムや QOP を知る方法と、それらの選択方法について、簡単に説明しています。


GSS-API における名前型

QOP とセキュリティーメカニズムのほかに、名前型を示すためにも OID が使用されます。名前型とは、関連する名前の形式を示すものです。たとえば、gss_import_name() 関数は主体の名前を文字列から gss_name_t 型に変換しますが、この関数は変換すべき文字列の形式を引数の 1 つとして受け取ります。たとえば、名前型が GSS_C_NT_HOSTBASED_SERVICE である場合、この関数は、入力された名前が service@host 形式であると判断します。名前型が GSS_C_NT_EXPORT_NAME である場合、この関数は GSS-API エクスポート名を期待します。アプリケーションは gss_inquire_names_for_mech() 関数を使用すると、指定したメカニズムで使用できる名前型を知ることができます。GSS-API によって使用される名前型の一覧については、名前型を参照してください。

GSS-API ステータスコード

すべての GSS-API 関数は、関数の成功または失敗に関する情報を提供する 2 種類のコードを返します。どちらの種類のステータスコードも OM_uint32 値として戻されます。

    次に、この 2 種類の戻りコードについて説明します。

  • メジャーステータスコード

      メジャーステータスコードは次のエラーを示します。

    • 汎用 GSS-API ルーチンエラー (ルーチンに無効なメカニズムを指定したなど)

    • 特定の GSS-API 言語バインディングに固有の呼び出しエラー (関数の引数が読み書きできない、引数の形式が間違っているなど)

    • 両方のタイプのエラー

    さらに、メジャーステータスコードは、ルーチンの状態に関する補足情報も提供できます。たとえば、処理が終了していない、トークンの送信順が間違っている、などを示すコードが返されます。何もエラーが発生しなかった場合、ルーチンは値が GSS_S_COMPLETE のメジャーステータス値を戻します。

    メジャーステータスコードは次のようにして返されます。

    OM_uint32 major_status ;    /* status returned by GSS-API */
    
    major_status = gss_generic_function(arg1, arg2 ...);

    メジャーステータス戻りコードはほかの OM_uint32 と同じように処理できます。たとえば、次のコードを考えます。

    OM_uint32 maj_stat;
    
    maj_sta = gss_generic_function(arg1, arg2 ...);
    
    if (maj_stat == GSS_CREDENTIALS_EXPIRED)
         <do something...>

    メジャーステータスコードは、マクロ GSS_ROUTINE_ERROR()GSS_CALLING_ERROR()、および GSS_SUPPLEMENTARY_INFO() で処理できます。GSS-API ステータスコード では、メジャーステータスコードの読み取り方法について説明しているほか、GSS-API ステータスコードの一覧を提供しています。

  • マイナーステータスコード

    マイナーステータスコードは実際のメカニズムから戻されます。これらのコードについては、このドキュメントでは具体的に説明しません。

    すべての GSS-API 関数は最初の引数として OM_uint32 型のマイナーステータスコードを受け取ります。関数が呼び出し元の関数に制御を戻す際に、その OM_uint32 引数にマイナーステータスコードが格納されます。次のコードを考えます。

    OM_uint32 *minor_status ;    /* status returned by mech */
    
    major_status = gss_generic_function(&minor_status, arg1, arg2 ...);

    致命的なエラーを示すメジャーステータスコードが返される場合でも、minor_status パラメータは GSS-API ルーチンによって必ず設定されます。その他のほとんどの出力パラメータには値が設定されません。ただし、ルーチンによって割り当てられた記憶領域へのポインタを返すべき出力パラメータには、NULL が設定されます。NULL は、記憶領域が実際には割り当てられなかったことを示します。このようなポインタに関連する長さフィールド (gss_buffer_desc 構造体を参照) は 0 に設定されます。そのような場合、アプリケーションはこれらのバッファーを解放する必要はありません。

GSS-API トークン

GSS-API における「流通」の基本単位は「トークン」です。GSS-API を使用するアプリケーションは、トークンを使用して互いに通信します。トークンは、データを交換したりセキュリティーを確立したりするために使われます。トークンは gss_buffer_t データ型として宣言されます。トークンはアプリケーションに対して不透明です。

トークンには、コンテキストレベルトークンメッセージ毎トークンの 2 種類があります。コンテキストレベルトークンは主に、コンテキストを確立する際、つまりコンテキストを起動して受け入れる際に使用されます。コンテキストレベルトークンは、コンテキストを管理する目的で、後になって渡されることがあります。

メッセージ毎トークンは、コンテキストが確立されたあとで使用されます。メッセージ毎トークンは、データ保護サービスを提供する目的で使用されます。たとえば、別のアプリケーションにメッセージを送信したいアプリケーションを考えます。そのアプリケーションは、GSS-API を使って暗号化識別子を生成し、それをメッセージに添付します。その識別子はトークンに格納されます。

メッセージ毎トークンは、メッセージとの関係において次のように考えることができます。「メッセージ」とは、アプリケーションがピアに送信するデータです。たとえば、ls コマンドは、ftp サーバーに送信されるメッセージになりえます。メッセージ毎トークンとは、そのメッセージに対して GSS-API が生成するオブジェクトのことです。メッセージ毎トークンの例としては、暗号タグや暗号化された形式のメッセージが挙げられます。ただし、後者の例は若干不正確です。暗号化されたメッセージはやはりメッセージであり、トークンではありません。トークンと呼べるのは、GSS-API によって生成された情報だけです。しかし、正式にではありませんが、メッセージメッセージ毎トークンは同じ意味で使用されることがあります。

    次の作業はアプリケーションの責任です。

  1. トークンを送受信すること。開発者はこのようなアクションを実行するために、通常、汎用的な読み取り関数と書き込み関数を作成する必要があります。Miscellaneous GSS-API Sample Functions ()send_token() 関数と その他の GSS-API 関数例 関数を参照してください。

  2. トークンの種類を区別し、それに応じてトークンを操作すること。

    トークンはアプリケーションに対して不透明であるため、アプリケーションは、あるトークンと別のトークンを区別できません。トークンの内容がわからなくても、アプリケーションはトークンの種類を区別できる必要があります。なぜなら、そうしないとトークンを適切な GSS-API 関数に渡せないからです。

      アプリケーションは、次の方法でトークンの種類を区別できます。

    • 状態によって。 (プログラムの制御フローを通じて)。たとえば、コンテキストを受け入れるために待機しているアプリケーションは、受信したトークンはコンテキストの確立に関係するものであると仮定します。ピアは、コンテキストが完全に確立されるまで、メッセージトークン (つまりデータ) の送信を行わないと予想されます。いったんコンテキストが確立されると、アプリケーションは新しいトークンがメッセージトークンであると仮定します。このようなトークンの処理方法は、非常に一般的なものです。後述のプログラム例でもこの方法を使用しています。

    • フラグによって。たとえば、トークンをピアに送信するための関数がアプリケーションに含まれている場合、そのアプリケーションにはトークンの種類を示すフラグを含めることができます。次のコードを考えます。

      gss_buffer_t token;     /* declare the token */
      OM_uint32 token_flag       /* flag for describing the type of token */
      
      <GSS-API 関数からトークンを取得>
      
      token_flag = MIC_TOKEN;     /* specify what kind of token it is */
      send_a_token(&token, token_flag);

      受信側のアプリケーションは、受信関数 (get_a_token() など) で token_flag 引数を検査します。

    • 明示的なタグ付けによって。アプリケーションはメタトークンを使用できます。メタトークンは、GSS-API 関数から受け取ったトークンを格納するためのユーザー定義の構造体です。メタトークンには、GSS-API から提供されたトークンの使用方法を示すユーザー定義フィールドが含まれます。

GSS-API におけるプロセス間トークン

GSS-API では、マルチプロセスアプリケーション内のあるプロセスから別のプロセスにセキュリティーコンテキストを渡せます。通常、アプリケーションはクライアントのコンテキストを受け入れます。アプリケーションはそのコンテキストをアプリケーション内のプロセス間で共有します。マルチプロセスアプリケーションについては、GSS-API におけるコンテキストのエクスポートとインポートを参照してください。

gss_export_context() 関数はプロセス間トークンを作成します。このトークンに含まれる情報を使えば、2 番目のプロセス内でコンテキストを再構築できます。あるプロセスから別のプロセスにプロセス間トークンを渡すのは、アプリケーションの責任です。この状況は、トークンを別のアプリケーションに渡すのがアプリケーションの責任であることに似ています。

プロセス間トークンには、鍵などの機密情報が含まれる可能性があります。必ずしもすべての GSS-API 実装がプロセス間トークンを暗号技術で保護するとは限りません。したがって、アプリケーションは、プロセス間トークンに保護を施したあとで交換を実施する必要があります。そうした保護は、gss_wrap() でトークンを暗号化するなどして実現します (ただし、暗号化が利用可能である場合)。


注 - 異なる GSS-API 実装間では、プロセス間トークンを転送できるとは限りません。