跳过导航链接 | |
退出打印视图 | |
Oracle Solaris 11 开发者安全性指南 Oracle Solaris 11 Information Library (简体中文) |
建立连接之后,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 是一个 int,用于指定 GSS-API 函数 gss_init_sec_context() 所返回的任何标志。
client_establish_context() 执行以下任务:
将服务名称转换为 GSSAPI 内部格式
在客户机和服务器之间循环执行令牌交换,直到建立完整的安全上下文为止
client_establish_context() 执行的首个任务是使用 gss_import_name() 将服务名称字符串转换为 GSS-API 内部格式。
示例 5-3 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。 第三个参数类型为 gss_OID,用于指明 send_tok 名称的格式。 此示例使用 GSS_C_NT_HOSTBASED_SERVICE,这表示服务采用 service@host 格式。 有关此参数的其他可能值,请参见名称类型。
将服务转换为 GSS-API 内部格式之后即可建立上下文。 为了尽可能提高可移植性,应当始终循环建立上下文。
进入循环之前,client_establish_context() 会初始化上下文和 token_ptr 参数。 使用 token_ptr 时需要进行选择。token_ptr 可以指向 send_tok(发送到服务器的令牌)或 recv_tok(服务器发回的令牌)。
在循环内部,需要检查两项:
gss_init_sec_context() 返回的状态
返回状态可捕捉任何可能要求循环异常中止的错误。当且仅当服务器还要发送另一个令牌时,gss_init_sec_context() 才会返回 GSS_S_CONTINUE_NEEDED。
gss_init_sec_context() 所生成的要发送到服务器的令牌的大小
如果令牌大小为零,则表示不再有可以发送到服务器的信息并且可以退出循环。 令牌大小可根据 token_ptr 来确定。
以下伪代码对该循环进行了说明:
do gss_init_sec_context() if no context was created exit with error; if the status is neither "complete" nor "in process" release the service namespace and exit with error; if there is a token to send to the server, that is, the size is nonzero send the token; if sending the token fails, release the token and service namespaces. Exit with error; release the namespace for the token that was just sent; if the context is not completely set up receive a token from the server; while the context is not complete该循环从调用 gss_init_sec_context() 开始,该函数使用以下参数:
要由基础机制设置的状态码。
凭证句柄。 此示例使用 GSS_C_NO_CREDENTIAL 充当缺省主体。
gss-context,表示要创建的上下文句柄。
服务的 target-name,如 GSS_API 内部名称。
oid,机制的 ID。
请求标志。 在本示例中,客户机请求执行以下操作:服务器对自身进行验证,打开消息重复功能,服务器根据请求充当代理。
对于上下文无时间限制。
没有请求使用通道绑定。
token_ptr,该参数指向要从服务器接收的令牌。
服务器实际使用的机制。 机制在此处设置为 NULL,这是因为应用程序不会使用该值。
&send_tok,gss_init_sec_context() 所创建的要发送到服务器的令牌。
返回标志。 设置为 NULL,这是因为在此示例中忽略了这些标志。
注 - 客户机在启动上下文之前无需获取凭证。 在客户端,凭证管理通过 GSS-API 以透明方式处理。 即,GSS-API 知道如何获取此机制为该主体创建的凭证。 因此,应用程序可以向 gss_init_sec_context() 传递缺省凭证。 但是在服务器端,服务器应用程序在接受上下文之前必须明确获取服务的凭证。 请参见获取凭证。
检查了上下文或其一部分是否存在,以及 gss_init_sec_context() 是否返回有效状态之后,connect_to_server() 会检查 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 已创建的任何令牌。
下面提供了用于建立上下文循环的源代码。
注 - 此示例的源代码也可以通过 Oracle 下载中心获取。 请参见 http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html。
示例 5-4 用于建立上下文的循环
/* * 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 样例函数。