本节介绍如何创建用于为 SASL 应用程序提供机制和其他服务的插件。
由于导出规程,对于非 Solaris 客户机/服务器机制插件,Solaris SASL SPI 不支持安全层。 因此,非 Solaris 客户机/服务器机制插件不能提供完整性和保密性服务。Solaris 客户机/服务器机制插件没有此限制。
SASL 服务提供者接口 (service provider interface, SPI) 可实现插件与 libsasl 库之间的通信。 SASL 插件通常作为共享库来实现。单个共享库可以具有一个或多个不同类型的 SASL 插件。位于共享库中的插件是由 libsasl 通过 dlopen(3C) 函数动态打开的。
还可以将插件静态绑定至调用 libsasl 的应用程序。使用 sasl_client_add_plugin() 函数或 sasl_server_add_plugin() 函数装入这些种类的插件,具体视应用程序是客户机还是服务器而定。
Solaris 操作系统中的 SASL 插件具有以下要求:
共享库中的插件必须位于有效的可执行对象文件(文件扩展名最好是 .so)中。
插件必须位于可验证的位置中。SASL_CB_VERIFYFILE 回调用于验证插件。
插件必须包含正确的入口点。
SASL 客户机的插件版本必须与 SASL 服务器的对应插件版本匹配。
插件需要能够成功初始化。
二进制类型的插件必须与 libsasl 的二进制类型匹配。
SASL 插件分为四种类别:
客户机机制插件
服务器机制插件
标准化插件
Auxprop 插件
sasl_client_init() 函数导致 SASL 客户机装入所有可用的客户机插件。sasl_server_init() 函数导致 SASL 服务器装入服务器插件、标准化插件和 auxprop 插件。调用 sasl_done() 时将卸载所有插件。
为查找插件,libsasl 使用 SASL_CB_GETPATH 回调函数或缺省路径。SASL_CB_GETPATH 返回以冒号分隔的目录列表,以供在其中查找插件。如果 SASL 消费方指定了 SASL_CB_GETPATH 回调,则 libsasl 将使用返回的搜索路径。否则,SASL 消费方可以使用与二进制类型对应的缺省路径:
32 位 SPARC 体系结构: /usr/lib/sasl
32 位 x86 体系结构: /usr/lib/sasl
64 位 SPARC 体系结构: /usr/lib/sasl/sparcv9
x64 体系结构: /usr/lib/sasl/amd64
在装入过程中,libsasl 将调用最新支持的插件版本。插件将返回该版本以及描述该插件的结构。如果该版本合格,则 libsasl 即可装入该插件。当前版本号 SASL_UTILS_VERSION 为 4。
初始化插件后,插件与 libsasl 之间的后续通信通过必须建立的结构执行。插件使用 sasl_utils_t 结构来调用 libsasl。libsasl 使用以下结构中的入口点与插件进行通信:
sasl_out_params_t
sasl_client_params_t
sasl_server_params_t
sasl_client_plug_t
sasl_server_plug_t
sasl_canonuser_plug_t
sasl_auxprop_plug_t
可以在 SASL 头文件中找到这些结构的源代码。这些结构将在下一节中进行介绍。
sasl_utils_t-sasl_utils_t 结构包含大量实用程序函数以及三种上下文:
此结构包含大量可为插件编写人员提供者便的实用程序函数。许多函数是指向 libsasl 中的公共接口的指针。插件不需要直接调用 libsasl,除非出于某种原因,插件需要是 SASL 消费方。
libsasl 将为 sasl_utils_t 创建三种上下文。
sasl_conn_t *conn
sasl_rand_t *rpool
void *getopt_context
在某些情况(如装入插件)下,sasl_utils_t 中的 conn 变量实际上与连接没有关联。在另外一些情况下,conn 是 SASL 消费方的 SASL 连接上下文。rpool 变量用于随机数生成函数。getopt_context 是应该与 getopt() 函数结合使用的上下文。
sasl_getopt_t(3SASL)、sasl_log_t(3SASL) 和 sasl_getcallback_t(3SASL)
sasl_out_params_t-libsasl 用于创建 sasl_out_params_t 结构并将该结构传递给客户机或服务器中的 mech_step()。此结构可以将以下信息传达给 libsasl: 验证状态、authid、authzid、maxbuf、协商的 ssf 以及数据编码和解码信息
sasl_client_params_t-libsasl 使用 sasl_client_params_t 结构将客户机状态传递给 SASL 客户机机制。客户机机制的 mech_new()、mech_step() 和 mech_idle() 入口点用于发送此状态数据。canon_user_client() 入口点还需要同时传递客户机状态。
sasl_server_params_t-sasl_server_params_t 结构在服务器端执行和 sasl_client_params_t 类似的功能。
客户机插件用于管理 SASL 协商的客户端。客户机插件通常随对应的服务器插件一同打包。客户机插件包含一种或多种客户端 SASL 机制。每种 SASL 客户机机制都支持验证、完整性和保密性(后两者是可选的)。每种机制都提供有关该机制的功能的信息:
最大 SSF
最大安全标志
插件功能
用于使用插件的回调和提示 ID
客户机插件必须导出 sasl_client_plug_init()。libsasl 将调用 sasl_client_plug_init() 来初始化客户机的插件。插件将返回 sasl_client_plug_t 结构。sasl_client_plug_t 将为 libsasl 提供以下用于调用机制的入口点:
mech_new()-客户机通过调用 sasl_client_start()(使用 mech_new())来启动连接。mech_new() 将执行特定于机制的初始化。如有必要,将分配连接上下文。
mech_step()-mech_step() 可通过 sasl_client_start() 和 sasl_client_step() 来进行调用。调用 mech_new() 后,mech_step() 将对客户端执行验证。如果验证成功,mech_step() 将返回 SASL_OK。如果需要更多数据,则将返回 SASL_CONTINUE。如果验证失败,则将返回 SASL 错误代码。如果出现错误,则将调用 seterror()。如果验证成功,则 mech_step() 必须返回包含相关安全层信息和回调的 sasl_out_params_t 结构。canon_user() 函数是此结构的一部分。客户机收到验证和授权 ID 时,必须调用 canon_user()。
mech_dispose()-mech_dispose() 是在安全关闭上下文时进行调用的。mech_dispose() 由 sasl_dispose() 进行调用。
mech_free()-mech_free() 是在 libsasl 关闭时进行调用的。插件的所有其余全局状态都是通过 mech_free() 释放的。
服务器插件用于管理 SASL 协商的服务器端。服务器插件通常随对应的客户机插件一同打包。服务器插件包含一种或多种服务器端 SASL 机制。每种 SASL 服务器机制都支持验证、完整性和保密性(后两者是可选的)。每种机制都提供有关该机制的功能的信息:
最大 SSF
最大安全标志
插件功能
用于使用插件的回调和提示 ID
服务器插件必须导出 sasl_server_plug_init()。libsasl 将调用 sasl_server_plug_init() 来初始化服务器的插件。插件将返回 sasl_server_plug_t 结构。sasl_server_plug_t 将为 libsasl 提供以下用于调用机制的入口点:
mech_new()-服务器通过调用 sasl_server_start()(使用 mech_new())来启动连接。mech_new() 将执行特定于机制的初始化。如有必要,mech_new() 将分配连接上下文。
mech_step()-mech_step() 可通过 sasl_server_start() 和 sasl_server_step() 来进行调用。调用 mech_new() 后,mech_step() 将对服务器端执行验证。如果验证成功,mech_step() 将返回 SASL_OK。如果需要更多数据,则将返回 SASL_CONTINUE。如果验证失败,则将返回 SASL 错误代码。如果出现错误,则将调用 seterror()。如果验证成功,则 mech_step() 必须返回包含相关安全层信息和回调的 sasl_out_params_t 结构。canon_user() 函数是此结构的一部分。服务器收到验证和授权 ID 时,必须调用 canon_user()。调用 canon_user() 函数将导致 propctx 被填充。标准化验证之前,应执行所有必需的辅助属性请求。标准化验证后,应执行授权 ID 查找。
返回 SASL_OK 之前,mech_step() 函数必须填充所有相关的 sasl_out_params_t 字段。这些字段可以执行以下函数:
doneflag-指示完整的交换
maxoutbuf-指示安全层的最大输出大小
mech_ssf-为安全层提供 SSF
encode()-由 sasl_encode()、sasl_encodev() 和 sasl_decode() 进行调用
decode()-由 sasl_encode()、sasl_encodev() 和 sasl_decode() 进行调用
encode_context()-由 sasl_encode()、sasl_encodev() 和 sasl_decode() 进行调用
decode_context()-由 sasl_encode()、sasl_encodev() 和 sasl_decode() 进行调用
mech_dispose()-mech_dispose() 是在安全关闭上下文时进行调用的。mech_dispose() 由 sasl_dispose() 进行调用。
mech_free()-mech_free() 是在 libsasl 关闭时进行调用的。插件的所有其余全局状态都是通过 mech_free() 释放的。
setpass() 可以设置用户口令。setpass() 使机制可以具有内部口令。
mech_avail() 由 sasl_listmech() 进行调用,目的是检查机制是否可用于给定用户。mech_avail() 可以创建新的上下文,进而避免对 mech_new() 的调用。只要性能不受影响,就应使用此方法创建上下文。
标准化插件为客户端和服务器端的验证和授权名称的备用标准化提供支持。sasl_canonuser_plug_init() 用于装入标准化插件。标准化插件具有以下要求:
必须将标准化的名称复制到输出缓冲区。
可以将同一输入缓冲区用作输出缓冲区。
当仅存在一个验证 ID 或授权 ID 时,标准化插件必须发挥作用。
用户标准化插件必须导出 sasl_canonuser_init() 函数。sasl_canonuser_init() 函数必须返回 sasl_canonuser_plug_t 以建立必要的入口点。用户标准化插件必须至少实现 sasl_canonuser_plug_t 结构的一个 canon_user_client() 成员或 canon_user_server() 成员。
Auxprop 插件为查找 SASL 服务器的 authid 和 authzid 的辅助属提供支持。例如,应用程序可能需要查找内部验证的用户口令。sasl_auxprop_plug_init() 函数用于初始化 auxprop 插件并返回 sasl_auxpropr_plug_t 结构。
要成功实现 auxprop 插件,必须实现 sasl_auxprop_plug_t 结构的 auxprop_lookup 成员。标准化用户名后,应使用标准化的用户名来调用 auxprop_lookup() 函数。随后,插件即可执行请求的辅助属性所需的所有查询。
Sun Microsystems, Inc. 当前不支持 auxprop 插件。
适当的错误报告可以帮助跟踪验证问题和其他调试问题。建议插件开发者使用 sasl_utils_t 结构中的 sasl_seterror() 回调为给定连接提供详细的错误信息。
在 SASL 中分配内存的一般规则是在不再需要已分配的任何内存时将其释放。遵循此规则可以提高性能和可移植性,而且可以避免内存泄漏。
插件机制可以设置客户机和服务器通过以下标志执行 SASL 会话的顺序:
SASL_FEAT_WANT_CLIENT_FIRST-客户端将开始交换。
SASL_FEAT_WANT_SERVER_LAST-服务器将最终数据发送到客户机。
如果未设置任何标志,则机制插件将在内部设置顺序。在这种情况下,机制必须同时检查客户机和服务器中需要发送的数据。请注意,只有在协议允许初始响应时,才有可能出现客户机首先发送的情况。
服务器最后发送的情况要求在步骤函数返回 SASL_OK 时插件可设置 *serverout。永远不让服务器最后发送的那些机制必须将 *serverout 设置为 NULL。始终让服务器最后发送的那些机制需要将 *serverout 指向成功数据。