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

GSS-API の重要な要素

ここでは、GSS-API の重要な概念である、 主体、GSS-API データ型、GSS-API 状態コード、および 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-API 関数に渡す前に gss_buffer_desc 構造体に変換しておく必要があります。次の例では、汎用的な GSS-API 関数を使って、送信前のメッセージに保護を適用しています。


例 4–1 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() を使って解放する必要がある点に注意してください。

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

GSS-API における名前

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

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 の文字列とそれに類するデータ」および例 4–2 を参照してください。この引数は、アプリケーションでの使用終了後、gss_release_buffer() を使って解放する必要があります。

input-name-type

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

output-name

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

次に示すのは、例 4–1 の汎用例に若干の変更を加えたものであり、gss_import_name() の使用法を示しています。まず、通常の文字列が gss_buffer_desc 構造体に挿入されています。次に、その文字列が、gss_import_name () によって gss_name_t 構造体内に格納されています。


例 4–2 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@companygss_name_t 構造体には、Kerberos v5 から提供されたその名前の 1 つのバージョンと、別の機構から提供された別のバージョンが含まれる可能性があります。関数 gss_canonicalize_name() は、入力として内部名と機構を受け取ります。また、gss_canonicalize_name() は、出力としてその機構に固有の単一バージョン名だけを含む別の内部名を返します。

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

図 4–3 内部名と機構名 (MN)

機構名が派生するプロセスを示しています。

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

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

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

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

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

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

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

図 4–4 名前の比較 (遅い)

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

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

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

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

  2. gss_canonicalize_name() を使用してクライアント名の機構名バージョンを生成します。

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

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

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

図 4–5 名前の比較 (速い)

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) は、次のようなデータを格納するときに使用します。

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


例 4–3 OID の構造体

typedef struct gss_OID_desc_struct {
        OM_uint32   length;
        void        *elements;
     } gss_OID_desc, *gss_OID;

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


例 4–4 OID セットの構造体

typedef struct gss_OID_set_desc_struct {
        size_t    count;
        gss_OID   elements;
     } gss_OID_set_desc, *gss_OID_set;


注意 – 注意 –

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


GSS-API における機構と QOP

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


注意 – 注意 –

セキュリティー機構または QOP を明示的に指定することは、GSS-API の使用目的に反します。そうした特定の選択は、アプリケーションの移植性を制限します。ほかの GSS-API 実装は、その QOP または機構を意図した方法でサポートしていない可能性があります。ただし、付録 C 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-API を使用するアプリケーションは、トークンを使用して互いに通信します。トークンは、データを交換したりセキュリティーを確立したりするために使われます。トークンは gss_buffer_t データ型として宣言されます。トークンはアプリケーションに対して不透明です。

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

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

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

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

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

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

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

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

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

      gss_buffer_t token;     /* declare the token */
      OM_uint32 token_flag       /* flag for describing the type of token */
      
      <get token from a GSS-API function>
      
      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 実装間では、プロセス間トークンを転送できるとは限りません。