接続が確立された後、call_server() は client_establish_context() 関数を使用して、セキュリティコンテキストを確立します。
int client_establish_context(s, service_name, deleg_flag, oid, &context, &ret_flags) |
s は、connect_to_server() で確立される接続を表すファイル記述子です。
service_name は要求するネットワークサービスです (nfs など)。
deleg_flag は、サーバーがクライアントのプロキシとして動作するかどうかを指定します。
oid は機構です。
context は作成されるコンテキストです。
ret_flags は GSS-API 関数 gss_init_sec_context() から戻されるフラグを指定します (型は int)。
コンテキストを起動するために、アプリケーションは gss_init_sec_context() 関数を使用します。ほとんどの GSS-API 関数と同様に、この関数でも名前は GSS-API 内部形式である必要があります。したがって、アプリケーションはまず、サービス名を文字列から内部形式に変換する必要があります。このためには、gss_import_name() を使用します。
maj_stat = gss_import_name(&min_stat, &send_tok, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &target_name); |
この関数は引数としてサービス名を受け取り (参照できない (不透明な) GSS-API バッファ send_tok に格納される)、GSS-API 内部名 target_name に変換します。send_tok は新しい gss_buffer_desc を宣言せず、領域を節約するために使用されます。3 番目の引数は gss_OID 型で、send_tok に格納されている名前の形式を示します。この場合は GSS_C_NT_HOSTBASED_SERVICE で、サービスの形式が service@host であることを意味します。この引数に有効なその他の値については、名前型を参照してください。
サービスを GSS-API 内部形式に変換した後、コンテキストを確立する作業に進むことができます。移植性を最大限にするには、コンテキストの確立を常にループとして実行する必要があります。コンテキストの起動 (クライアント)を参照してください。
まず、アプリケーションはコンテキストを NULL に初期化します。
*gss_context = GSS_C_NO_CONTEXT; |
token_ptr = GSS_C_NO_BUFFER; |
次に、アプリケーションはループに入ります。ループは 2 つのことを検査しながら進みます。gss_init_sec_context() から戻される状態と、サーバーに送信されるトークンのサイズ (この値も gss_init_sec_context() で生成される) です。トークンのサイズが 0 の場合、サーバーがクライアントからこれ以上のトークンを期待していないことを意味します。次に、このループの疑似コードを示します。
do{ gss_init_sec_context() if (コンテキストが全く確立されなかった場合) エラーを出力して終了する if (状態が「完了」または「処理中」のどちらでもない場合 サービスの名前空間を解放し、エラーを出力して終了する if (サーバーに送信するトークンがある場合。つまり、サイズが 0 以外の場合) トークンを送信する if (トークンの送信が失敗した場合) トークンとサービスの名前空間を解放し、エラーを出力して終了する 先ほど送信したトークンの名前空間を解放する if (コンテキストの確立が完了していない場合 サーバーからトークンを受け取る } while (コンテキストが完全になるまで) |
次に、gss_init_sec_context() への呼び出しを示します。
do { maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, gss_context, target_name oid GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | deleg_flag, 0, NULL, &send_tok, ret_flags, NULL); |
実際の機構が設定する状態コード。
資格ハンドル。デフォルトのプリンシパルとして動作させるために、GSS_C_NO_CREDENTIAL を使用します。
(gss_context) 作成するコンテキストハンドル。
(target_name) GSS_API 内部名形式のサービス。
(oid) 機構。
要求フラグ。この場合、クライアントは、
a) サーバーが自分自身を認証すること
b) メッセージの複製をオンにすること
c) サーバーがプロキシとして動作すること (要求された場合)
を要求します。
コンテキストの時間制限はありません。
チャネルバインディングの要求はありません。
(token_ptr) サーバーから受け取るトークンへのポインタ (ある場合)。
サーバーが実際に使用する機構。アプリケーションがこの値に関与していないため、ここでは NULL に設定されています。
(&send_tok) サーバーに送信するために gss_init_sec_context() が作成するトークン。
戻りフラグ。ここでは無視するため、NULL に設定されています。
コンテキストを起動する前には、クライアントは資格を獲得する必要がないことに注意してください。クライアント側では、資格の管理は GSS-API によって透過的に処理されます。つまり、(通常はログイン時に) プリンシパルのために機構が作成した資格をどのように取得するかを、GSS-API は知っているということです。このため、アプリケーションは gss_init_sec_context() にデフォルトの資格を渡しています。しかし、サーバー側では、サーバーアプリケーションはコンテキストを受け入れる前に、サービスの資格を明示的に獲得する必要があります。資格の獲得を参照してください。
コンテキスト (完成されている必要はない) があることと、gss_init_sec_context() が有効な状態を戻したことを検査した後、アプリケーションは gss_init_sec_context() がサーバーに送信すべきトークンをコンテキストに渡しているかどうかを調べます。渡していない場合、これは、トークンが (これ以上) 必要でないことをサーバーが示しているためです。渡している場合、トークンをサーバーに送信します。トークンの送信が失敗した場合、トークンの名前空間とサービスを解放し、(エラーで) 終了します。トークンの存在をチェックするには、トークンの長さ (サイズ) を調べることに注意してください。
if (send_tok_length != 0) { if (send_token(s, &send_tok) < 0) { (void) gss_release_buffer(&min_stat, &send_tok); (void) gss_release_name(&min_stat, &target_name); return -1; } } |
send_token() は GSS-API 関数ではなく、ユーザーが作成した基本的なファイル書き込み関数です。詳細は、send_token()を参照してください。GSS-API 自身はトークンを送受信しないことに注意してください。GSS-API が作成したトークンを送受信するのは、呼び出し側アプリケーションの責任です。
サーバーが (これ以上) 送信するトークンを持っていない場合、gss_init_sec_context() は GSS_S_COMPLETE を戻します。つまり、gss_init_sec_context() がこの値を戻さなかった場合、アプリケーションはまだ受け取るトークンがサーバーに残っていると判断できます。受け取りが失敗した場合、アプリケーションはサービスの名前空間を解放し、(エラーで) 終了します。
if (maj_stat == GSS_S_CONTINUE_NEEDED) { if (recv_token(s, &recv_tok) < 0) { (void) gss_release_name(&min_stat, &target_name); return -1; |
最後に、プログラムはトークンのポインタをリセットします。そして、コンテキストが完全に確立されるまで、ループを繰り返します。したがって、do ループの終了は次のようになります。
} while (maj_stat == GSS_S_CONTINUE_NEEDED); |