JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
Oracle Solaris 10 セキュリティー開発者ガイド     Oracle Solaris 10 1/13 Information Library (日本語)
search filter icon
search icon

ドキュメントの情報

はじめに

1.  Oracle Solaris の開発者向けセキュリティー機能 (概要)

2.  特権付きアプリケーションの開発

3.  PAM アプリケーションおよび PAM サービスの記述

4.  GSS-API を使用するアプリケーションの記述

5.  GSS-API クライアント例

6.  GSS-API サーバー例

GSS-API サーバー例の概要

GSS-API サーバー例の構造

GSS-API サーバー例の実行

GSSAPI サーバー例: main() 関数

資格の獲得

inetd の検査

クライアントからのデータの受信

コンテキストの受け入れ

メッセージのラップ解除

メッセージへの署名とメッセージの返送

test_import_export_context() 関数の使用

GSS-API サーバー例のクリーンアップ

7.  SASL を使用するアプリケーションの記述

8.  Oracle Solaris 暗号化フレームワークの紹介

9.  ユーザーレベルの暗号化アプリケーションとプロバイダの記述

10.  スマートカードフレームワークの使用

A.  C ベース の GSS-API プログラム例

B.  GSS-API リファレンス

C.  OID の指定

D.  SASL ソースコード例

E.  SASL リファレンス

F.  暗号化プロバイダのパッケージ化と署名

用語集

索引

クライアントからのデータの受信

inetd の検査後、gss-server プログラムは、プログラムの主な作業を担う sign_server() を呼び出します。sign_server() はまず、server_establish_context() を呼び出してコンテキストを確立します。

sign_server() は次のタスクを実行します。

以降では、これらのタスクについて順次説明します。sign_server() 関数のソースコードを、次に示します。

例 6-3 sign_server() 関数

int sign_server(s, server_creds)
     int s;
     gss_cred_id_t server_creds;
{
     gss_buffer_desc client_name, xmit_buf, msg_buf;
     gss_ctx_id_t context;
     OM_uint32 maj_stat, min_stat;
     int i, conf_state, ret_flags;
     char    *cp;
     
     /* Establish a context with the client */
     if (server_establish_context(s, server_creds, &context,
                  &client_name, &ret_flags) < 0)
     return(-1);

     printf("Accepted connection: \"%.*s\"\n",
         (int) client_name.length, (char *) client_name.value);
     (void) gss_release_buffer(&min_stat, &client_name);

     for (i=0; i < 3; i++)
         if (test_import_export_context(&context))
             return -1;

     /* Receive the sealed message token */
     if (recv_token(s, &xmit_buf) < 0)
         return(-1);

     if (verbose && log) {
         fprintf(log, "Sealed message token:\n");
         print_token(&xmit_buf);
     }

     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("unsealing 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);

     fprintf(log, "Received message: ");
     cp = msg_buf.value;
     if ((isprint(cp[0]) || isspace(cp[0])) &&
         (isprint(cp[1]) || isspace(cp[1]))) {
         fprintf(log, "\"%.*s\"\n", msg_buf.length, msg_buf.value);
     } else {
         printf("\n");
         print_token(&msg_buf);
     }

     /* Produce a signature block for the message */
     maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT,
                  &msg_buf, &xmit_buf);
     if (maj_stat != GSS_S_COMPLETE) {
         display_status("signing message", maj_stat, min_stat);
         return(-1);
     }

     (void) gss_release_buffer(&min_stat, &msg_buf);

     /* Send the signature block to the client */
     if (send_token(s, &xmit_buf) < 0)
         return(-1);

     (void) gss_release_buffer(&min_stat, &xmit_buf);

     /* Delete context */
     maj_stat = gss_delete_sec_context(&min_stat, &context, NULL);
     if (maj_stat != GSS_S_COMPLETE) {
         display_status("deleting context", maj_stat, min_stat);
         return(-1);
     }

     fflush(log);

     return(0);
}

コンテキストの受け入れ

一般に、コンテキストの確立時には、クライアントとサーバー間で一連のトークンが交換されます。プログラムの移植性を保つには、コンテキストの受け入れと起動の両方を、ループ内で実行する必要があります。コンテキスト受け入れループは、コンテキスト起動ループと非常によく似ています (ある意味で逆ですが)。「サーバーとのセキュリティーコンテキストの確立」と比較してみてください。

server_establish_context() 関数のソースコードを、次に示します。

例 6-4 server_establish_context() 関数

/*
 * Function: server_establish_context
 *
 * Purpose: establishes a GSS-API context as a specified service with
 * an incoming client, and returns the context handle and associated
 * client name
 *
 * Arguments:
 *
 *      s               (r) an established TCP connection to the client
 *      service_creds   (r) server credentials, from gss_acquire_cred
 *      context         (w) the established GSS-API context
 *      client_name     (w) the client's ASCII name
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * Any valid client request is accepted.  If a context is established,
 * its handle is returned in context and the client name is returned
 * in client_name and 0 is returned.  If unsuccessful, an error
 * message is displayed and -1 is returned.
 */
int server_establish_context(s, server_creds, context, client_name, ret_flags)
     int s;
     gss_cred_id_t server_creds;
     gss_ctx_id_t *context;
     gss_buffer_t client_name;
     OM_uint32 *ret_flags;
{
     gss_buffer_desc send_tok, recv_tok;
     gss_name_t client;
     gss_OID doid;
     OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
     gss_buffer_desc    oid_name;

     *context = GSS_C_NO_CONTEXT;
     
     do {
          if (recv_token(s, &recv_tok) < 0)
               return -1;

          if (verbose && log) {
              fprintf(log, "Received token (size=%d): \n", recv_tok.length);
              print_token(&recv_tok);
          }

          maj_stat =
               gss_accept_sec_context(&acc_sec_min_stat,
                                      context,
                                      server_creds,
                                      &recv_tok,
                                      GSS_C_NO_CHANNEL_BINDINGS,
                                      &client,
                                      &doid,
                                      &send_tok,
                                      ret_flags,
                                      NULL,     /* ignore time_rec */
                                      NULL);    /* ignore del_cred_handle */

          (void) gss_release_buffer(&min_stat, &recv_tok);

          if (send_tok.length != 0) {
               if (verbose && log) {
                    fprintf(log,
                          "Sending accept_sec_context token (size=%d):\n",
                          send_tok.length);
                    print_token(&send_tok);
               }
               if (send_token(s, &send_tok) < 0) {
                    fprintf(log, "failure sending token\n");
                    return -1;
               }

               (void) gss_release_buffer(&min_stat, &send_tok);
          }
          if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
               display_status("accepting context", maj_stat,
                              acc_sec_min_stat);
               if (*context == GSS_C_NO_CONTEXT)
                       gss_delete_sec_context(&min_stat, context,
                                              GSS_C_NO_BUFFER);
               return -1;
          }

          if (verbose && log) {
              if (maj_stat == GSS_S_CONTINUE_NEEDED)
                  fprintf(log, "continue needed...\n");
              else
                  fprintf(log, "\n");
              fflush(log);
          }
     } while (maj_stat == GSS_S_CONTINUE_NEEDED);

     /* display the flags */
     display_ctx_flags(*ret_flags);

     if (verbose && log) {
         maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name);
         if (maj_stat != GSS_S_COMPLETE) {
             display_status("converting oid->string", maj_stat, min_stat);
             return -1;
         }
         fprintf(log, "Accepted connection using mechanism OID %.*s.\n",
                 (int) oid_name.length, (char *) oid_name.value);
         (void) gss_release_buffer(&min_stat, &oid_name);
     }

     maj_stat = gss_display_name(&min_stat, client, client_name, &doid);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("displaying name", maj_stat, min_stat);
          return -1;
     }
     maj_stat = gss_release_name(&min_stat, &client);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("releasing name", maj_stat, min_stat);
          return -1;
     }
     return 0;
}

sign_server() 関数は、コンテキストを受け入れる際に、次のソースコードを使って server_establish_context() を呼び出します。

/* Establish a context with the client */
     if (server_establish_context(s, server_creds, &context,
                  &client_name, &ret_flags) < 0)
    return(-1);

server_establish_context() 関数はまず、クライアントがコンテキスト起動中に送信したトークンを探します。GSS-API 自身はトークンの送受信を行わないため、これらのタスクを行うにはプログラムが独自のルーチンを持つ必要があります。サーバーは、次のように recv_token() を使ってトークンを受信します。

     do {
          if (recv_token(s, &recv_tok) < 0)
               return -1;

次に、server_establish_context() は、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,     /* ignore time_rec */
                                      NULL);    /* ignore del_cred_handle */

gss_accept_sec_context()maj_statGSS_S_CONTINUE_NEEDED を設定している限り、受け入れループは継続します (エラーの場合を除く)。maj_stat の値がその値でも 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);
          }

メッセージのラップ解除

コンテキストの受け入れ後、sign_server() は、クライアントから送信されてきたメッセージを受け取ります。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 をプログラムが知る必要がないことを示します。

メッセージへの署名とメッセージの返送

この時点で、sign_server() 関数はメッセージに署名する必要があります。メッセージへの署名には、メッセージのメッセージ整合性コード (MIC) のクライアントへの返送が伴います。メッセージを返送することで、メッセージの送信とラップ解除が正常に完了したことをクライアントに証明できます。MIC を取得するために、sign_server() は関数 gss_get_mic() を使用します。

maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT,
                            &msg_buf, &xmit_buf);

gss_get_mic() は、msg_buf 内のメッセージに基づいて MIC を生成し、その結果を xmit_buf に格納します。次に、サーバーは send_token() で MIC をクライアントに返送します。クライアントは、gss_verify_mic() でその MIC を検証します。「GSS-API クライアントにおける署名ブロックの読み取りと検証」を参照してください。

最後に、sign_server() はいくつかのクリーンアップを実行します。sign_server() は、gss_release_buffer() で GSS-API バッファーの msg_bufxmit_buf を解放します。続いて、sign_server() は、gss_delete_sec_context() でコンテキストを破棄します。

test_import_export_context() 関数の使用

GSS-API を使用すると、コンテキストをエクスポートおよびインポートできます。これにより、マルチプロセスプログラムの異なるプロセス間でコンテキストを共有できます。sign_server() には概念検証用の関数 test_import_export_context() があります。この関数は、コンテキストのエクスポートとインポートがどのように機能するかを示します。test_import_export_context() は、コンテキストをプロセス間で渡すわけではありません。test_import_export_context() は、コンテキストをエクスポートするのにかかった時間を表示し、次に、インポートするのにかかった時間を表示します。test_import_export_context() は、実際には機能しない関数ですが、GSS-API のインポートおよびエクスポート機能をどのように使えばよいかを示しています。また、test_import_export_context() は、コンテキスト操作時のタイムスタンプの使い方も示しています。

test_import_export_context() のソースコードを、次に示します。

例 6-5 test_import_export_context()

int test_import_export_context(context)
        gss_ctx_id_t *context;
{
        OM_uint32       min_stat, maj_stat;
        gss_buffer_desc context_token, copied_token;
        struct timeval tm1, tm2;

        /*
         * Attempt to save and then restore the context.
         */
        gettimeofday(&tm1, (struct timezone *)0);
        maj_stat = gss_export_sec_context(&min_stat, context, &context_token);
        if (maj_stat != GSS_S_COMPLETE) {
                display_status("exporting context", maj_stat, min_stat);
                return 1;
        }
        gettimeofday(&tm2, (struct timezone *)0);
        if (verbose && log)
                fprintf(log, "Exported context: %d bytes, %7.4f seconds\n",
                        context_token.length, timeval_subtract(&tm2, &tm1));
        copied_token.length = context_token.length;
        copied_token.value = malloc(context_token.length);
        if (copied_token.value == 0) {
            fprintf(log, "Couldn't allocate memory to copy context token.\n");
            return 1;
        }
        memcpy(copied_token.value, context_token.value, copied_token.length);
        maj_stat = gss_import_sec_context(&min_stat, &copied_token, context);
        if (maj_stat != GSS_S_COMPLETE) {
                display_status("importing context", maj_stat, min_stat);
                return 1;
        }
        free(copied_token.value);
        gettimeofday(&tm1, (struct timezone *)0);
        if (verbose && log)
                fprintf(log, "Importing context: %7.4f seconds\n",
                        timeval_subtract(&tm1, &tm2));
        (void) gss_release_buffer(&min_stat, &context_token);
        return 0;
}