Go to main content
Oracle® Solaris 11 セキュリティー開発者ガイド

印刷ビューの終了

更新: 2016 年 11 月
 
 

サーバーとのセキュリティーコンテキストの確立

接続が確立されたあと、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 内部形式に変換します

  • セキュリティーコンテキストが完了するまで、クライアントとサーバー間のトークン交換ループを実行します

サービス名の GSS-API 形式への変換

client_establish_context() が実行する最初のタスクは、gss_import_name() を使ってサービス名の文字列を GSS-API 内部形式に変換することです。

使用例 13  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 セキュリティーコンテキストの確立

サービスの 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 によって作成されたトークンを送受信することは、呼び出し元のアプリケーションの責任です。

コンテキスト確立ループのソースコードを、次に示します。

使用例 14  コンテキスト確立用のループ
/*
 * 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 関数例を参照してください。