建立上下文通常涉及到在客户机和服务器之间执行一系列令牌交换。为了保持程序的可移植性,应当在循环中同时执行上下文接受和上下文初始化。用于接受上下文的循环与用于建立上下文的循环非常相似,但是二者的方向相反。请与Establishing a Security Context With the Server进行比较。
以下源代码说明了 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 是要提供的服务的凭证(请参见Acquiring Credentials)。
recv_tok 是 recv_token() 从客户机接收的令牌。
GSS_C_NO_CHANNEL_BINDINGS 是一个标志,表示不使用通道绑定(请参见Using Channel Bindings in 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); }