サービスの資格を獲得した後、サーバープログラムは、ユーザーが inetd
を使用するように指定しているかどうかを調べ
(概要: main() (サーバー)を参照)、次いで sign_server()
を呼び出します。sign_server() はプログラムの主な作業を行います。sign_server() が最初に行うことは、server_establish_context() を呼び出してコンテキストを確立することです。
ここでは、inetd
については説明しません。基本的に、inetd
が指定された場合、プログラムは標準入力で sign_server()
を呼び出します。そうでない場合、プログラムはソケットを作成し、接続を受け入れ、次に、その接続で sign_server()
を呼び出します。
sign_server() は次の作業を行います。
コンテキストを受け入れます。
データをラップ解除します。
データに署名します。
データを戻します。
コンテキストの確立では、クライアントとサーバー間で一連のトークンが交換されます。したがって、プログラムの移植性を保持するために、コンテキストの受け入れとコンテキストの起動はループで行われる必要があります。実際、コンテキストを受け入れるためのループとコンテキストを確立するためのループは (まったく逆ですが) 非常によく似ています。(コンテキストの確立と比較してください。)
サーバーが最初に行うことは、クライアントがコンテキスト起動プロセスの一部として送信したトークンを探すことです。GSS-API 自身はトークンを送受信しないことを思い出してください。したがって、この作業を行うには、プログラムは独自のルーチンを持つ必要があります。サーバーがトークンの受信用に使用するルーチンを、この例では recv_token() としています。詳細は、recv_token()を参照してください。
do { if (recv_token(s, &recv_tok) < 0) return -1; |
次に、プログラムは GSS-API 関数 gss_accept_sec_context() を呼び出します。
maj_stat = gss_accept_sec_context(&min_stat, context, server_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &client, &doid, &send_tok, ret_flags, NULL, /* time_rec を無視する */ NULL); /* del_cred_handle を無視する */ |
次に、各引数について説明します。
min_stat は実際の機構から戻されるエラー状態です。
context は確立されているコンテキストです。
server_creds は提供されているサーバーの資格です (資格の獲得を参照)。
recv_tok は recv_token() でクライアントから受信したトークンです。
GSS_C_NO_CHANNEL_BINDINGS はチャネルバインディングを使用しないことを示すフラグです (チャネルバインディングを参照)。
client はクライアント名 (ASCII 文字) です。
oid は機構です (OID 形式)。
send_tok はクライアントに送信するトークンです。
ret_flags は、コンテキストがサポートするオプション (メッセージ順序検出など) を示すさまざまなフラグです。
NULL は、コンテキストが有効である期間の長さにも、サーバーがクライアントのプロキシとして動作できるかどうかにも、プログラムが関与していないことを示します。
gss_accept_sec_context() が maj_stat に GSS_S_CONTINUE_NEEDED を設定している限り、受け入れループは継続します (エラーの場合を除く)。maj_stat の値が GSS_S_CONTINUE_NEEDED でも GSS_S_COMPLETE でもない場合、問題が発生し、ループが終了したことを示します。
クライアントに返送するトークンが存在する場合、gss_accept_sec_context() は send_tok の長さ (正の数) を戻します。次に行うことは、送信するトークンがあるかどうかを調べて、もしあれば、そのトークンを送信することです。
if (send_tok.length != 0) { . . . if (send_token(s, &send_tok) < 0) { fprintf(log, "failure sending token\n"); return -1; } (void) gss_release_buffer(&min_stat, &send_tok); } |
コンテキストを受け入れた後、サーバーはクライアントから送信されたメッセージを受信します。GSS-API はこの作業を行う関数を提供しないため、プログラムは独自の関数 recv_token() を使用します。
if (recv_token(s, &xmit_buf) < 0) return(-1); |
メッセージは暗号化されている可能性があるため、プログラムは GSS-API 関数 gss_unwrap() でメッセージをラップ解除します。
maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf, &conf_state, (gss_qop_t *) NULL); if (maj_stat != GSS_S_COMPLETE) { display_status("unwrapping message", maj_stat, min_stat); return(-1); } else if (! conf_state) { fprintf(stderr, "Warning! Message not encrypted.\n"); } (void) gss_release_buffer(&min_stat, &xmit_buf); |
gss_unwrap() は、recv_token() が xmit_buf に格納したメッセージを入力として受け取り、そのメッセージを変換し、その結果を msg_buf に格納します。gss_unwrap() への 2 つの引数に注目してください。 conf_state は、このメッセージに機密性が適用されたかどうか (つまり、メッセージが暗号化されているかどうか) を示すフラグです。次に、最後の NULL は、メッセージを保護するときに使用された QOP にプログラムが関与していないことを示します。
最後にサーバーがすることは、メッセージに署名することです。つまり、メッセージの MIC (メッセージ整合性コード。メッセージに関連付けられた一意なタグ) をクライアントに戻すことによって、メッセージの送信とラップ解除が正常に完了したことをクライアントに証明します。このためには、プログラムは gss_get_mic() 関数を使用します。
maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT, &msg_buf, &xmit_buf); |
この関数は msg_buf 内のメッセージを調べて、そのメッセージから MIC を生成し、その結果を xmit_buf に格納します。次に、サーバーは send_token() で MIC をクライアントに返送します。すると、クライアントは gss_verify_mic() で MIC を検証します。メッセージの検証を参照してください。
最後に、sign_server() はいくつかのクリーンアップを実行します。つまり、gss_release_buffer() で GSS-API バッファの msg_buf と xmit_buf を解放し、次に、gss_delete_sec_context() でコンテキストを無効にします。
コンテキストのエクスポートとインポートで説明したとおり、GSS-API を使用すると、コンテキストをエクスポートおよびインポートできます。通常、これを行う理由は、マルチプロセスプログラムにおける異なるプロセス間でコンテキストを共有することです。
sign_server() には検証用の関数 test_import_export_context() があります。この関数は、コンテキストのエクスポートとインポートがどのように機能するかを示します。この関数は実際にはコンテキストをプロセス間で渡しません。コンテキストをエクスポートするのにかかった時間を表示し、次に、インポートするのにかかった時間を表示するだけです。実際には機能しませんが、この関数は GSS-API のインポート関数とエクスポート関数をどのように使用するかを示すとともに、コンテキストの操作に関連するタイムスタンプをどのように使用するかの参考にもなります。test_import_export_context() の詳細は、test_import_export_context()を参照してください。