JavaScript is required to for searching.
跳过导航链接
退出打印视图
Oracle Solaris 11 开发者安全性指南     Oracle Solaris 11 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

1.  面向开发者的 Oracle Solaris 安全(概述)

2.  开发特权应用程序

3.  编写 PAM 应用程序和服务

4.  编写使用 GSS-API 的应用程序

5.  GSS-API 客户机示例

6.  GSS-API 服务器示例

GSSAPI 服务器示例概述

GSSAPI 服务器示例结构

运行 GSSAPI 服务器示例

GSSAPI 服务器示例:main() 函数

获取凭证

检查 inetd

从客户机接收数据

接受上下文

展开消息

消息的签名和返回

使用 test_import_export_context() 函数

在 GSSAPI 服务器示例中清除

7.  编写使用 SASL 的应用程序

8.  Oracle Solaris 加密框架介绍

9.  编写用户级加密应用程序和提供者

10.  Oracle Solaris 密钥管理框架介绍

A.  基于 C 的 GSS-API 样例程序

B.  GSS-API 参考

C.  指定 OID

D.  SASL 示例的源代码

E.  SASL 参考表

词汇表

索引

从客户机接收数据

检查 inetd 之后,gss-server 程序会调用 sign_server(),该函数用于执行程序的主要工作。sign_server() 首先通过调用 server_establish_context() 来建立上下文。

sign_server() 可执行以下任务:

这些任务将在以下各节中进行介绍。 以下源代码说明了 sign_server() 函数。


注 - 此示例的源代码也可以通过 Oracle 下载中心获取。 请参见 http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html


示例 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() 函数。


注 - 此示例的源代码也可以通过 Oracle 下载中心获取。 请参见 http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html


示例 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_stat 设置为 GSS_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() 的两个参数。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,并将该 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() 的源代码如以下示例所示。


注 - 此示例的源代码也可以通过 Oracle 下载中心获取。 请参见 http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html


示例 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;
}