跳过导航链接 | |
退出打印视图 | |
Oracle Solaris 11 开发者安全性指南 Oracle Solaris 11.1 Information Library (简体中文) |
检查 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 */
min_stat 是基础机制返回的错误状态。
context 是所建立的上下文。
server_creds 是要提供的服务的凭证(请参见获取凭证)。
recv_tok 是 recv_token() 从客户机接收的令牌。
GSS_C_NO_CHANNEL_BINDINGS 是一个标志,表示不使用通道绑定(请参见在 GSS-API 中使用通道绑定)。
client 是客户机的 ASCII 名称。
oid 表示机制(采用 OID 格式)。
send_tok 是要发送到客户机的令牌。
ret_flags 是用于指明上下文是否支持指定选项的各种标志,如 message-sequence-detection。
两个 NULL 参数表示程序无需知道上下文将保持有效的时间长度或者服务器是否可以充当客户机的代理。
只要 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_buf 和 xmit_buf。然后,sign_server() 会使用 gss_delete_sec_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; }