次の節では、より重要な表示できる GSS-API データ型について説明します。詳細は、GSS-API データ型と値を参照してください。
割り当てられたすべてのデータ領域を解放するのは呼び出し元アプリケーションの責任です。
int のサイズはプラットフォームによって異なるため、GSS-API は次の整数型を提供します。
OM_uint32
これは、32 ビットの符号なし整数です。
GSS-API はすべてのデータを内部形式で処理するため、文字列は GSS-API 関数に渡す前に GSS-API 形式に変換しておく必要があります。GSS-API は文字列を gss_buffer_desc 構造体で処理します。 gss_buffer_t は gss_buffer desc 構造体へのポインタです。
typedef struct gss_buffer_desc_struct { size_t length; void *value; } gss_buffer_desc *gss_buffer_t; |
したがって、文字列は GSS-API 関数に渡す前に gss_buffer_desc 構造体に変換しておく必要があります。ここでは、メッセージを受け取ってなんらかの方法で処理する (たとえば、転送する前にメッセージに保護を適用するなど)、次のような汎用的な 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); |
終了時には gss_release_buffer() で input_msg_buffer を解放する必要があることに注意してください。
gss_buffer_desc オブジェクトは文字列だけに使用されるわけではありません。たとえば、トークンも gss_buffer_desc オブジェクトとして処理されます。GSS-API トークンを参照してください。
「名前」はプリンシパルを指します。つまり、joe@company や nfs@machinename などのように、人、マシン、またはアプリケーションを指します。GSS-API では、名前は gss_namae_t オブジェクトとして格納され、アプリケーションでは意識する必要はありません。名前は gss_import_name() 関数によって gss_buffer_t オブジェクトから gss_name_t 形式に変換されます。インポートされたすべての名前には関連する「名前型」が割り当てられます。名前型とは、その名前の形式の種類を示すものです。名前型についての詳細は、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) |
実際の機構から戻される状態コード。状態コードを参照してください。
インポートされた名前が格納される gss_buffer_desc 構造体。アプリケーションはこの構造体を明示的に割り当てる必要があります。例 1–2、および 文字列および類似のデータを参照してください。使用し終わったとき、この引数は gss_release_buffer() で解放する必要があります。
input_name_buffer の形式を示す gss_OID。名前型を参照してください。また、有効な名前型のリストについては、名前型を参照してください。
名前を受け取る gss_name_t 構造体。
次に、例 1-1 で使用した汎用例を少しだけ変更します。ここでは、どのように gss_import_name() を使用するかを示します。まず、通常の文字列を gss_buffer_desc 構造体に変換して、次に、gss_import_name() で gss_name_t 構造体に変換します。
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 つのバージョンが生成されます。たとえば、「joe@company」の gss_name_t 構造体は Kerberos v5 用の名前と他の機構用の名前を持つなどです。GSS-API は gss_canonicalize_name() という関数を提供します。この関数は、内部名 (つまり、gss_name_t 構造体) と機構を入力として受け取り、その機構に特定な名前のバージョンを 1 つだけ持つ別の内部名 (これも gss_name_t 構造体) を出力します。
このような機構に固有な名前のことを「機構名 (Mechanism Name: MN)」と呼びます。機構名は、機構自身の名前を指すのではなく、その機構が生成したプリンシパルの名前を指すため、すこし紛らわしいかもしれません。図 1–3 に、このプロセスを図示します。
なぜこのような関数が便利なのでしょうか。ここでは、サーバーがクライアントから名前を受け取り、その名前をアクセス制御リストから検索する例を考えます。アクセス制御リスト (Access Control List: ACL) とは、特定のアクセス権を備えたプリンシパルのリストのことです。このためには、次のような方法が考えられます。
gss_import_name() で、クライアント名を GSS-API 内部形式でインポートします (まだインポートされていない場合)。
サーバーの中には、内部形式で名前を受け取るものもあります。この場合、この手順は必要ありません。特に、サーバーがクライアント独自の名前を検索する場合です。コンテキストの起動中、クライアント独自の名前は内部形式で渡されます。
gss_import_name() で、各 ACL 名を インポートします。
gss_compare_name() で、インポートした ACL 名とインポートしたクライアント名をそれぞれ比較します。
図 1–4 に、このプロセスを示します。ここでは手順 1 が必要であると仮定します。
この手順は、クライアント名と比較する名前の数が少ない場合には有効です。しかし、非常に処理が遅いため、大きなリストを比較するときには不都合です。ACL 名ごとに gss_import_name() と gss_compare_name() を実行すると、膨大な CPU サイクルが必要です。したがって、次のような方法を使用します。
gss_import_name() で、クライアント名をインポートします (まだインポートされていない場合)。
前述の方法と同様に、サーバーが内部形式で名前を受け取る場合、この手順は必要ありません。
gss_canonicalize_name() で、クライアント名の MN を生成します。
gss_export_name() で、「エクスポート名」を生成します。エクスポート名とは、クライアント名の連続する文字列バージョンのことです。
memcmp() で、エクスポートされたクライアント名を ACL 内のすべての名前と比較します。memcmp() は高速で、オーバーヘッドの少ない関数です。
図 1–5 に、このプロセスを示します。ここでも、サーバーがクライアントから名前をインポートする必要があると仮定します。
gss_export_name() は機構名 (MN) を期待するため、あらかじめ、クライアント名に対して gss_canonicalize_name() を実行する必要があります。
詳細は、gss_canonicalize_name(3GSS)、gss_export_name(3GSS)、および gss_import_name(3GSS) のマニュアルページを参照してください。
オブジェクト識別子 (Object Identifier: OID) は、次のようなデータを格納するときに使用します。セキュリティ機構、QOP の値 (保護品質の値)、および名前型など。OID は GSS-API の ggss_OID_desc 構造体に格納されます。GSS-API は次のような gss_OID_desc 構造体へのポインタ (gss_OID) を提供します。
typedef struct gss_OID_desc_struct { OM_uint32 length; void *elements; } gss_OID_desc, *gss_OID; |
さらに、gss_OID_set_desc 構造体は1つまたは複数の 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
を使用すると、アプリケーションはどの実際のセキュリティ機構を使用するかを選択できます。しかし、アプリケーションは可能な限り、GSS-API が選択したデフォルトの機構を使用するべきです。同様に、GSS-API
を使用すると、アプリケーションはどの QOP (保護品質) でデータを保護するかを指定できます。QOP とは、データの暗号化や暗号識別タグの生成に使用されるアルゴリズムのことです。デフォルトの機構を表すには、機構または
QOP を期待する関数に値 GSS_C_NULL_OID
を引数として渡します。
セキュリティ機構または QOP を明示的に指定するとアプリケーションの移植性を制限してしまうため、GSS-API を使用する目的が多かれ少なかれ損なわれます。他の実装の GSS-API がその機構または QOP をサポートしなかったり、サポートする方法や範囲が異なる場合があるためです。ただし、付録 C 「OID の指定」 では、使用できる機構と QOP を示して、その選択方法を示しています。
QOP とセキュリティ機構の他に、OID でも名前型を指定できます。名前型とは、関連する名前の形式を示すものです。たとえば、gss_import_name() 関数はプリンシパルの名前を文字列から gss_name_t 型に変換しますが、この関数は変換すべき文字列の形式を引数の 1 つとして受け取ります。たとえば、名前型が GSS_C_NT_HOSTBASED_SERVICE である場合、gss_import_name() 関数は入力された名前が "service@host" 形式 (たとえば、"nfs@swim2birds") であることが分かります。また、たとえば名前型が GSS_C_NT_EXPORT_NAME である場合、gss_import_name() 関数は入力された名前が GSS-API エクスポート名であることが分かります。アプリケーションは gss_inquire_names_for_mech() 関数を使用すると、指定した機構で使用できる名前型を知ることができます。GSS-API が使用する名前型のリストについては、名前型を参照してください。