Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1275571
  • 博文数量: 554
  • 博客积分: 10425
  • 博客等级: 上将
  • 技术积分: 7555
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-09 09:49
文章分类

全部博文(554)

文章存档

2012年(1)

2011年(1)

2009年(8)

2008年(544)

分类:

2008-04-14 09:59:02


GSSAPI 服务器示例:main() 函数
第6 章• GSS-API 服务器示例129
示例6–1 gss-server 示例:main() (续)
} else if (strcmp(*argv, "-once") == 0) {
once = 1;
} else if (strcmp(*argv, "-inetd") == 0) {
do_inetd = 1;
} else if (strcmp(*argv, "-logfile") == 0) {
argc--; argv++;
if (!argc) usage();
log = fopen(*argv, "a");
display_file = log;
if (!log) {
perror(*argv);
exit(1);
}
} else
break;
argc--; argv++;
}
if (argc != 1)
usage();
if ((*argv)[0] == ’-’)
usage();
GSSAPI 服务器示例:main() 函数
130 Solaris 开发者安全性指南• 2006 年11 月
示例6–1 gss-server 示例:main() (续)
service_name = *argv;
/* Acquire service credentials. */
if (server_acquire_creds(service_name, &server_creds) < 0)
return -1;
if (do_inetd) {
close(1);
close(2);
/* Sign and return message. */
sign_server(0, server_creds);
close(0);
} else {
int stmp;
if ((stmp = create_socket(port)) >= 0) {
do {
/* Accept a TCP connection */
if ((s = accept(stmp, NULL, 0)) < 0) {
perror("accepting connection");
continue;
}
GSSAPI 服务器示例:main() 函数
第6 章• GSS-API 服务器示例131
示例6–1 gss-server 示例:main() (续)
/* this return value is not checked, because there’s
not really anything to do if it fails */
sign_server(s, server_creds);
close(s);
} while (!once);
close(stmp);
}
}
/* Close down and clean up. */
(void) gss_release_cred(&min_stat, &server_creds);
/*NOTREACHED*/
(void) close(s);
return 0;
}
获取凭证
凭证由基础机制创建,而不是由客户机应用程序、服务器应用程序或GSS-API 创建。客户
机程序通常具有在登录时获取的凭证。服务器需要始终明确获取凭证。
gss-server 程序包含server_acquire_creds() 函数,该函数用于获取要提供的服务的凭
证。server_acquire_creds() 将该服务的名称和要使用的安全机制用作输入,然后返回该服
务的凭证。
server_acquire_creds() 使用GSS-API 函数gss_acquire_cred() 来获取服务器所提供的服务
的凭证。server_acquire_creds() 访问gss_acquire_cred() 之前必须执行以下两个任务:
获取凭证
132 Solaris 开发者安全性指南• 2006 年11 月
1. 检查机制列表并将该列表缩减成单个机制以获取凭证。
如果多个机制可以共享单个凭证,gss_acquire_cred() 函数将返回所有这些机制的凭
证。因此,gss_acquire_cred() 会将一组机制用作输入。(请参见第83 页中的“在
GSS-API 中使用凭证”。)但是,在大多数情况(包括此情况)下,单个凭证可能并不
适用于多个机制。gss-server 程序中会在命令行上指定单个机制或使用缺省机制。因
此,第一个任务是确保传递给gss_acquire_cred() 的那组机制中包含单个机制(缺省机
制或其他机制),如下所示:
if (mechOid != GSS_C_NULL_OID) {
desiredMechs = &mechOidSet;
mechOidSet.count = 1;
mechOidSet.elements = mechOid;
} else
desiredMechs = GSS_C_NULL_OID_SET;
GSS_C_NULL_OID_SET 表示应当使用缺省机制。
2. 将服务名称转换为GSS-API 格式。
由于gss_acquire_cred() 会接受gss_name_t 结构形式的服务名称作为参数,因此必须将
服务名称转换为这种格式。可使用gss_import_name() 函数执行此转换。与所有
GSS-API 函数一样,此函数要求参数采用GSS-API 类型,因此必须首先将服务名称复制
到GSS-API 缓冲区,如下所示:
name_buf.value = service_name;
name_buf.length = strlen(name_buf.value) + 1;
maj_stat = gss_import_name(&min_stat, &name_buf,
(gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name);
if (maj_stat != GSS_S_COMPLETE) {
display_status("importing name", maj_stat, min_stat);
if (mechOid != GSS_C_NO_OID)
gss_release_oid(&min_stat, &mechOid);
return -1;
}
获取凭证
第6 章• GSS-API 服务器示例133
另请注意非标准函数gss_release_oid() 的用法。
输入是name_buf 中字符串形式的服务名称。输出是一个指向gss_name_t 结构
server_name 的指针。第三个参数GSS_C_NT_HOSTBASED_SERVICE 是name_buf 中字符串的
名称类型。在这种情况下,名称类型表示应当将字符串解释为 格式的服
务。
执行这些任务之后,服务器程序可以调用gss_acquire_cred():
maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
desiredMechs, GSS_C_ACCEPT,
server_creds, NULL, NULL);
 min_stat 是函数返回的错误代码。
 server_name 是服务器的名称。
 0 表示程序无需知道凭证的最长生命周期。
 desiredMechs 是一组应用该凭证的机制。
 GSS_C_ACCEPT 表示凭证只能用于接受安全上下文。
 server_creds 是该函数返回的凭证句柄。
 NULL,NULL表示该程序无需知道所使用的特定机制或凭证将保持有效的时间长度。
以下源代码说明了server_acquire_creds() 函数。
注– 此示例的源代码也可以通过Sun 下载中心获取。请访问

示例6–2 server_acquire_creds() 函数的样例代码
/*
* Function:server_acquire_creds
*
* Purpose:imports a service name and acquires credentials for it
*
* Arguments:
*
* service_name (r) the ASCII service name
mechType (r) the mechanism type to use
获取凭证
134 Solaris 开发者安全性指南• 2006 年11 月
示例6–2 server_acquire_creds() 函数的样例代码(续)
* server_creds (w) the GSS-API service credentials
*
* Returns:0 on success, -1 on failure
*
* Effects:
*
* The service name is imported with gss_import_name, and service
* credentials are acquired with gss_acquire_cred. If either operation
* fails, an error message is displayed and -1 is returned; otherwise,
* 0 is returned.
*/
int server_acquire_creds(service_name, mechOid, server_creds)
char *service_name;
gss_OID mechOid;
gss_cred_id_t *server_creds;
{
gss_buffer_desc name_buf;
gss_name_t server_name;
OM_uint32 maj_stat, min_stat;
gss_OID_set_desc mechOidSet;
gss_OID_set desiredMechs = GSS_C_NULL_OID_SET;
if (mechOid != GSS_C_NULL_OID) {
获取凭证
第6 章• GSS-API 服务器示例135
示例6–2 server_acquire_creds() 函数的样例代码(续)
desiredMechs = &mechOidSet;
mechOidSet.count = 1;
mechOidSet.elements = mechOid;
} else
desiredMechs = GSS_C_NULL_OID_SET;
name_buf.value = service_name;
name_buf.length = strlen(name_buf.value) + 1;
maj_stat = gss_import_name(&min_stat, &name_buf,
(gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name);
if (maj_stat != GSS_S_COMPLETE) {
display_status("importing name", maj_stat, min_stat);
if (mechOid != GSS_C_NO_OID)
gss_release_oid(&min_stat, &mechOid);
return -1;
}
maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
desiredMechs, GSS_C_ACCEPT,
server_creds, NULL, NULL);
if (maj_stat != GSS_S_COMPLETE) {
display_status("acquiring credentials", maj_stat, min_stat);
获取凭证
136 Solaris 开发者安全性指南• 2006 年11 月
示例6–2 server_acquire_creds() 函数的样例代码(续)
return -1;
}
(void) gss_release_name(&min_stat, &server_name);
return 0;
}
检查inetd
获取服务的凭证之后,可以使用gss-server 来查看用户是否指定了inetd。main 函数按如
下方式检查inetd:
if (do_inetd) {
close(1);
close(2);
如果用户指定了使用inetd,则程序将关闭标准输出和标准错误。然后,gss-server 会针对
标准输入调用sign_server(),inetd 将使用该函数传递连接。否则,gss-server 会创建一
个套接字,使用TCP 函数accept() 接受该套接字的连接,然后针对accept() 返回的文件描
述符调用sign_server()。
如果未使用inetd,程序会创建连接和上下文,直到终止程序为止。但是,如果用户指定了
-once 选项,则循环将在首次连接之后终止。
从客户机接收数据
检查inetd 之后,gss-server 程序会调用sign_server(),该函数用于执行程序的主要工
作。sign_server() 首先通过调用server_establish_context() 来建立上下文。
sign_server() 可执行以下任务:
 接受上下文
 展开数据
 对数据进行签名
从客户机接收数据
第6 章• GSS-API 服务器示例137
 返回数据
这些任务将在以下各节中进行介绍。以下源代码说明了sign_server() 函数。
注– 此示例的源代码也可以通过Sun 下载中心获取。请访问

示例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);
从客户机接收数据
138 Solaris 开发者安全性指南• 2006 年11 月
示例6–3 sign_server() 函数(续)
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);
从客户机接收数据
第6 章• GSS-API 服务器示例139
示例6–3 sign_server() 函数(续)
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)
从客户机接收数据
140 Solaris 开发者安全性指南• 2006 年11 月
示例6–3 sign_server() 函数(续)
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);
}
接受上下文
建立上下文通常涉及到在客户机和服务器之间执行一系列令牌交换。为了保持程序的可移
植性,应当在循环中同时执行上下文接受和上下文初始化。用于接受上下文的循环与用于
建立上下文的循环非常相似,但是二者的方向相反。请与第109 页中的“建立与服务器的
安全上下文”进行比较。
以下源代码说明了server_establish_context() 函数。
注– 此示例的源代码也可以通过Sun 下载中心获取。请访问

从客户机接收数据
第6 章• GSS-API 服务器示例141
示例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.
*/
从客户机接收数据
142 Solaris 开发者安全性指南• 2006 年11 月
示例6–4 server_establish_context() 函数(续)
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);
}
从客户机接收数据
第6 章• GSS-API 服务器示例143
示例6–4 server_establish_context() 函数(续)
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);
}
从客户机接收数据
144 Solaris 开发者安全性指南• 2006 年11 月
示例6–4 server_establish_context() 函数(续)
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);
}
从客户机接收数据
第6 章• GSS-API 服务器示例145
示例6–4 server_establish_context() 函数(续)
} 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) {
从客户机接收数据
146 Solaris 开发者安全性指南• 2006 年11 月
示例6–4 server_establish_context() 函数(续)
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,
从客户机接收数据
第6 章• GSS-API 服务器示例147
&doid,
&send_tok,
ret_flags,
NULL, /* ignore time_rec */
NULL); /* ignore del_cred_handle */
 min_stat 是基础机制返回的错误状态。
 context 是所建立的上下文。
 server_creds 是要提供的服务的凭证(请参见第132 页中的“获取凭证”)。
 recv_tok 是recv_token() 从客户机接收的令牌。
 GSS_C_NO_CHANNEL_BINDINGS 是一个标志,表示不使用通道绑定(请参见第89 页中的“
在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;
}
从客户机接收数据
148 Solaris 开发者安全性指南• 2006 年11 月
(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() 函数:
从客户机接收数据
第6 章• GSS-API 服务器示例149
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。请参见第123 页中的“读取和检验GSS-API 客户机中的签名块”。
最后,sign_server() 会执行一些清除操作。sign_server() 会使用gss_release_buffer()
来释放GSS-API 缓冲区msg_buf 和xmit_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() 是一个虚
构的函数,但可用于指示如何使用GSS-API 导入和导出函数,并且还可以指示如何使用时
间标记来处理上下文。
test_import_export_context() 的源代码如以下示例所示。
注– 此示例的源代码也可以通过Sun 下载中心获取。请访问

示例6–5test_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.
从客户机接收数据
150 Solaris 开发者安全性指南• 2006 年11 月
示例6–5 test_import_export_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;
}
从客户机接收数据
第6 章• GSS-API 服务器示例151
示例6–5 test_import_export_context() (续)
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;
}
在GSSAPI 服务器示例中清除
返回到main() 函数中,应用程序会使用gss_release_cred() 来删除服务凭证。如果已经为
机制指定了OID,则程序会使用gss_release_oid() 删除该OID 并退出。
(void) gss_release_cred(&min_stat, &server_creds);
在GSSAPI 服务器示例中清除
152 Solaris 开发者安全性指南• 2006 年11 月
编写使用SASL的应用程序
SASL(Simple Authentication and Security Layer,简单验证和安全层)是一个安全框架。
SASL(发音为"sassel")为基于连接的协议提供验证服务以及完整性和保密性服务(可
选)。本章包含以下主题:
 第153 页中的“简单验证安全层(Simple Authentication Security Layer, SASL) 介绍”
 第165 页中的“SASL 示例”
 第171 页中的“服务提供者的SASL”
简单验证安全层(Simple Authentication Security Layer,
SASL) 介绍
SASL 为应用程序和共享库的开发者提供了用于验证、数据完整性检查和加密的机制。开发
者可通过SASL 对通用API 进行编码。此方法避免了对特定机制的依赖性。SASL 特别适用于
使用IMAP、SMTP、ACAP 和LDAP 协议的应用程序,因为这些协议全都支持SASL。RFC
2222 中说明了SASL。
SASL 库基础
SASL 库称为libsasl。libsasl 是一个框架,允许正确编写的SASL 消费方应用程序使用系
统中可用的所有SASL 插件。术语插件是指为SASL 提供服务的对象。插件位于libsasl 的外
部。SASL 插件可用于验证和安全性、名称标准化以及辅助属性(如口令)的查找。加密算
法存储在插件中,而不是libsasl 中。
libsasl 为消费方应用程序和库提供应用编程接口(application programming interface, API)。
服务提供者接口(service provider interface, SPI) 是为插件提供的,用于为libsasl 提供服务。
libsasl 不能识别网络或协议。相应地,应用程序必须负责在客户机与服务器之间发送和接
收数据。
SASL 对用户使用两个重要的标识符。验证ID (authid) 是用于验证用户的用户ID。验证ID
授予用户系统访问权限。授权ID (userid) 用于检查是否允许用户使用特定选项。
7第7 章
153
SASL 客户机应用程序和SASL 服务器应用程序将协商公用的SASL 机制和安全级别。通常,
SASL 服务器应用程序会将其可接受的验证机制的列表发送给客户机。随后SASL 客户机应
用程序便可决定哪种验证机制最能满足其要求。此后,客户机与服务器使用双方同意的验
证机制,对它们之间交换的由SASL 提供的验证数据进行验证。此交换将持续下去,直到验
证成功完成、失败或被客户机或服务器中止。
在验证过程中,SASL 验证机制可以协商安全层。如果已选择安全层,则必须在SASL 会话
期间使用该层。
SASL 体系结构
下图显示了基本的SASL 体系结构。
图7–1 SASL体系结构
客户机和服务器应用程序通过SASLAPI 调用其libsasl 的本地副本。libsasl 通过SASL 服
务提供者接口(service provider interface, SPI) 与SASL 机制进行通信。
安全机制
安全机制插件为libsasl 提供安全服务。以下是安全机制提供的一些典型功能:
 在客户端进行验证
 在服务器端进行验证
 完整性,即检查传输的数据是否保持不变
 保密性,即对传输的数据进行加密和解密
SASL 安全强度因子
SSF(即安全强度因子)指示SASL 保护的强度。如果该机制支持安全层,则客户机与服务
器会协商SSF。SSF 的值基于执行SASL 协商之前指定的安全属性。如果协商结果是非零
SSF,则验证完成后,客户机和服务器都需要使用该机制的安全层。SSF 由具有以下值之一
的整数表示:
简单验证安全层(Simple Authentication Security Layer, SASL) 介绍
154 Solaris 开发者安全性指南• 2006 年11 月
 0 -无保护。
 1 -仅限于完整性检查。
 >1 -支持验证、完整性和保密性。数字表示加密密钥长度。
保密性和完整性操作是通过安全机制执行的。libsasl 可以协调这些请求。
注– 在协商过程中,SASL客户机会选择具有最大SSF 的机制。但是,实际所选的SASL机制
可能随后会协商较小的SSF。
SASL 中的通信
应用程序通过libsasl API 与libsasl 进行通信。libsasl 可通过应用程序注册的回调方式请
求其他信息。应用程序不会直接调用插件,而只是通过libsasl 进行调用。一般情况下,插
件会调用libsasl 框架的插件,随后这些插件调用应用程序的回调。SASL 插件还可以直接
调用应用程序,不过应用程序不知道调用来自插件还是来自libsasl。
回调在以下几个方面非常有用。
 libsasl 可以使用回调获取完成验证所需的信息。
 libsasl 消费方应用程序可以使用回调更改插件和配置数据的搜索路径、验证文件以及
更改各种缺省行为。
 服务器可以使用回调来更改授权策略、提供不同的口令验证方法以及获取口令更改信
息。
 客户机和服务器可以使用回调来指定错误消息的语言。
应用程序可以注册两种回调:全局回调和会话回调。此外,libsasl 定义了大量用于为不同
种类的回调注册的回调标识符。如果未注册给定类型的回调,则libsasl 将执行缺省操作。
会话回调将覆盖全局回调。如果为给定ID 指定了会话回调,则不会为该会话调用全局回
调。某些回调必须是全局的,因为这些回调发生在会话之外。以下实例要求使用全局回调

 确定要装入的插件的搜索路径
 验证插件
 配置数据的位置
 记录错误消息
 对libsasl 或其插件的其他全局配置
可以使用给定SASL 回调ID 的NULL回调函数来注册SASL 回调。NULL回调函数指示装配
客户机的目的是为了提供所需的数据。所有的SASL 回调ID 都以前缀SASL_CB_ 开头。
SASL 提供以下可供客户机或服务器使用的回调:
SASL_CB_GETOPT 获取SASL 选项。选项用于修改libsasl (3LIB) 和相关插件的行
为。可由客户机或服务器使用。
SASL_CB_LOG 设置libsasl 及其插件的日志记录函数。缺省行为是使用
syslog。
 
 
以上文章转自于 : http://developers.sun.com.cn/
阅读(664) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~