ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
Oracle Solaris 11 セキュリティーサービス開発ガイド Oracle Solaris 11.1 Information Library (日本語) |
1. Oracle Solaris の開発者向けセキュリティー機能 (概要)
3. PAM アプリケーションおよび PAM サービスの記述
クライアント側におけるその他の GSS-API コンテキスト操作
GSS-API クライアントにおける署名ブロックの読み取りと検証
8. Oracle Solaris 暗号化フレームワークの紹介
接続が確立されたあと、call_server() は、次のように関数 client_establish_context() を使ってセキュリティーコンテキストを作成します。
if (client_establish_context(s, service-name, deleg-flag, oid, &context, &ret-flags) < 0) { (void) close(s); return -1; }
s は、connect_to_server() で確立された接続を表すファイル記述子です。
service-name は、要求されたネットワークサービスです。
deleg-flag は、サーバーがクライアントのプロキシとして動作できるかどうかを指定します。
oid は機構です。
context は作成されるコンテキストです。
ret-flags は、GSS-API 関数 gss_init_sec_context() から戻される任意のフラグを表す int です。
client_establish_context() は次のタスクを実行します。
サービス名を GSS-API 内部形式に変換します
セキュリティーコンテキストが完了するまで、クライアントとサーバー間のトークン交換ループを実行します
client_establish_context() が実行する最初のタスクは、gss_import_name() を使ってサービス名の文字列を GSS-API 内部形式に変換することです。
例 5-3 client_establish_context() – サービス名の変換
/* * Import the name into target_name. Use send_tok to save * local variable space. */ send_tok.value = service_name; send_tok.length = strlen(service_name) + 1; maj_stat = gss_import_name(&min_stat, &send_tok, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &target_name); if (maj_stat != GSS_S_COMPLETE) { display_status("parsing name", maj_stat, min_stat); return -1; }
gss_import_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 内部形式への変換が完了すると、コンテキストを確立できます。移植性を最大にするには、コンテキストの確立を常にループとして実行する必要があります。
ループに入る前に、client_establish_context() は、コンテキストと token_ptr パラメータを初期化します。token_ptr の使用には選択肢があります。token_ptr がポイントできるのは、サーバーに送信するトークンである send_tok、サーバーから返送されてきたトークンである recv_tok のいずれかです。
ループの内側では、次の 2 つの項目が検査されます。
gss_init_sec_context() から戻されるステータス
戻りステータスは、ループの異常終了を要求する可能性のあるすべてのエラーを捕捉します。gss_init_sec_context() が GSS_S_CONTINUE_NEEDED を戻すのは、別の送信すべきトークンがサーバー側に存在する場合に限ります。
サーバーに送信すべきトークンのサイズ (gss_init_sec_context() によって生成される)
トークンサイズ 0 は、サーバーに送信できる情報がこれ以上存在しないことと、ループが終了可能であることを意味します。トークンサイズは token_ptr で決定されます。
次に、このループの擬似コードを示します。
do gss_init_sec_context() if (コンテキストが作成されなかった場合) エラーを出力して終了する if (ステータスが「完了」または「処理中」のどちらでもない場合) サービスの名前空間を解放し、エラーを出力して終了する if (サーバーに送信するトークンがある場合、つまりサイズが 0 以外の場合) トークンを送信する if (トークンの送信が失敗した場合) トークンとサービスの名前空間を解放し、エラーを出力して終了する 送信し終わったトークンの名前空間を解放する if (コンテキストの確立が完了していない場合) サーバーからトークンを受信する while (コンテキストが完了していない)ループの最初で、gss_init_sec_context() が呼び出されます。この関数の引数は次のとおりです。
実際の機構が設定するステータスコード。
資格ハンドル。例ではデフォルトの主体として動作させるために、GSS_C_NO_CREDENTIAL を使用します。
gss-context は、作成されるコンテキストハンドルを表します。
GSS_API 内部名としてのサービスの target-name。
oid は機構の ID です。
要求フラグ。この場合にクライアントが要求することは、サーバーが自分自身を認証すること、メッセージの複製をオンにすること、要求された場合にサーバーがプロキシとして動作すること、のいずれかです。
コンテキストの時間制限はありません。
チャネルバインディングの要求はありません。
token_ptr は、サーバーから受け取るトークンを指します。
サーバーが実際に使用する機構。アプリケーションがこの値を使用しないため、ここでは NULL に設定されています。
&send_tok は、gss_init_sec_context() がサーバーに送信するために作成するトークンです。
戻りフラグ。この例では無視するため、NULL に設定されています。
注 - クライアントは、コンテキストの起動前に資格を取得する必要はありません。クライアント側では、資格の管理は GSS-API によって透過的に処理されます。つまり、この主体のためにこの機構が作成した資格をどのように取得するかを、GSS-API は知っているということです。このため、アプリケーションは gss_init_sec_context() にデフォルトの資格を渡しています。しかし、サーバー側では、サーバーアプリケーションはコンテキストを受け入れる前に、サービスの資格を明示的に獲得する必要があります。「資格の獲得」を参照してください。
connect_to_server() は、コンテキストまたはその一部が存在しており、かつ gss_init_sec_context() が有効なステータスを戻していることを確認したあと、gss_init_sec_context() がサーバーに送信すべきトークンを提供しているかどうかを検査します。トークンが存在しない場合、それはトークンがこれ以上必要ないことを、サーバーが示していると考えられます。トークンが提供された場合、そのトークンをサーバーに送信する必要があります。トークンの送信に失敗した場合、トークンとサービスの名前空間を決定できないため、connect_to_server() が終了します。次のアルゴリズムは、トークンの長さを調べることでトークンの存在の有無を検査しています。
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() 関数は、トークンをファイル記述子に書き込みます。send_token() は、正常終了時に 0 を、エラー時に –1 を戻します。GSS-API 自身はトークンの送受信を行いません。GSS-API によって作成されたトークンを送受信することは、呼び出し元のアプリケーションの責任です。
コンテキスト確立ループのソースコードを、次に示します。
注 - このソースコード例は、Oracle ダウンロードセンターからダウンロードすることも可能です。http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html を参照してください。
例 5-4 コンテキスト確立用のループ
/* * Perform the context establishment loop. * * On each pass through the loop, token_ptr points to the token * to send to the server (or GSS_C_NO_BUFFER on the first pass). * Every generated token is stored in send_tok which is then * transmitted to the server; every received token is stored in * recv_tok, which token_ptr is then set to, to be processed by * the next call to gss_init_sec_context. * * GSS-API guarantees that send_tok's length will be non-zero * if and only if the server is expecting another token from us, * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if * and only if the server has another token to send us. */ token_ptr = GSS_C_NO_BUFFER; *gss_context = GSS_C_NO_CONTEXT; 1234567890123456789012345678901234567890123456789012345678901234567890123456 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, /* no channel bindings */ token_ptr, NULL, /* ignore mech type */ &send_tok, ret_flags, NULL); /* ignore time_rec */ if (gss_context == NULL){ printf("Cannot create context\n"); return GSS_S_NO_CONTEXT; } if (token_ptr != GSS_C_NO_BUFFER) (void) gss_release_buffer(&min_stat, &recv_tok); if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) { display_status("initializing context", maj_stat, min_stat); (void) gss_release_name(&min_stat, &target_name); return -1; } if (send_tok.length != 0){ fprintf(stdout, "Sending init_sec_context token (size=%ld)...", send_tok.length); 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; } } (void) gss_release_buffer(&min_stat, &send_tok); if (maj_stat == GSS_S_CONTINUE_NEEDED) { fprintf(stdout, "continue needed..."); if (recv_token(s, &recv_tok) < 0) { (void) gss_release_name(&min_stat, &target_name); return -1; } token_ptr = &recv_tok; } printf("\n"); } while (maj_stat == GSS_S_CONTINUE_NEEDED);
send_token() と recv_token() の動作方法についての詳細は、「その他の GSS-API 関数例」を参照してください。