Solaris 开发者安全性指南

简单验证安全层 (Simple Authentication Security Layer, SASL) 介绍

SASL 为应用程序和共享库的开发者提供了用于验证、数据完整性检查和加密的机制。开发者可通过 SASL 对通用 API 进行编码。此方法避免了对特定机制的依赖性。SASL 特别适用于使用 IMAP、SMTP、ACAP 和 LDAP 协议的应用程序,因为这些协议全都支持 SASL。RFC 2222 中说明了 SASL。

SASL 库基础

SASL 库称为 libsasllibsasl 是一个框架,允许正确编写的 SASL 消费方应用程序使用系统中可用的所有 SASL 插件。术语插件是指为 SASL 提供服务的对象。插件位于 libsasl 的外部。SASL 插件可用于验证和安全性、名称标准化以及辅助属性(如口令)的查找。加密算法存储在插件中,而不是 libsasl 中。

libsasl 为消费方应用程序和库提供应用编程接口 (application programming interface, API)。服务提供者接口 (service provider interface, SPI) 是为插件提供的,用于为 libsasl 提供服务。libsasl 不能识别网络或协议。相应地,应用程序必须负责在客户机与服务器之间发送和接收数据。

SASL 对用户使用两个重要的标识符。验证 ID (authid) 是用于验证用户的用户 ID。验证 ID 授予用户系统访问权限。授权 ID (userid) 用于检查是否允许用户使用特定选项。

SASL 客户机应用程序和 SASL 服务器应用程序将协商公用的 SASL 机制和安全级别。通常,SASL 服务器应用程序会将其可接受的验证机制的列表发送给客户机。随后 SASL 客户机应用程序便可决定哪种验证机制最能满足其要求。此后,客户机与服务器使用双方同意的验证机制,对它们之间交换的由 SASL 提供的验证数据进行验证。此交换将持续下去,直到验证成功完成、失败或被客户机或服务器中止。

在验证过程中,SASL 验证机制可以协商安全层。如果已选择安全层,则必须在 SASL 会话期间使用该层。

SASL 体系结构

下图显示了基本的 SASL 体系结构。

图 7–1 SASL 体系结构

图中显示了主要的 SASL 元素如何采用客户机/服务器关系一同工作。

客户机和服务器应用程序通过 SASL API 调用其 libsasl 的本地副本。libsasl 通过 SASL 服务提供者接口 (service provider interface, SPI) 与 SASL 机制进行通信。

安全机制

安全机制插件为 libsasl 提供安全服务。以下是安全机制提供的一些典型功能:

SASL 安全强度因子

SSF(即安全强度因子)指示 SASL 保护的强度。如果该机制支持安全层,则客户机与服务器会协商 SSF。SSF 的值基于执行 SASL 协商之前指定的安全属性。如果协商结果是非零 SSF,则验证完成后,客户机和服务器都需要使用该机制的安全层。SSF 由具有以下值之一的整数表示:

保密性和完整性操作是通过安全机制执行的。libsasl 可以协调这些请求。


注 –

在协商过程中,SASL 客户机会选择具有最大 SSF 的机制。但是,实际所选的 SASL 机制可能随后会协商较小的 SSF。


SASL 中的通信

应用程序通过 libsasl API 与 libsasl 进行通信。libsasl 可通过应用程序注册的回调方式请求其他信息。应用程序不会直接调用插件,而只是通过 libsasl 进行调用。一般情况下,插件会调用 libsasl 框架的插件,随后这些插件调用应用程序的回调。SASL 插件还可以直接调用应用程序,不过应用程序不知道调用来自插件还是来自 libsasl

回调在以下几个方面非常有用。

应用程序可以注册两种回调:全局回调和会话回调。此外,libsasl 定义了大量用于为不同种类的回调注册的回调标识符。如果未注册给定类型的回调,则 libsasl 将执行缺省操作。

会话回调将覆盖全局回调。如果为给定 ID 指定了会话回调,则不会为该会话调用全局回调。某些回调必须是全局的,因为这些回调发生在会话之外。以下实例要求使用全局回调:

可以使用给定 SASL 回调 ID 的 NULL 回调函数来注册 SASL 回调。NULL 回调函数指示装配客户机的目的是为了提供所需的数据。所有的 SASL 回调 ID 都以前缀 SASL_CB_ 开头。

SASL 提供以下可供客户机或服务器使用的回调:

SASL_CB_GETOPT

获取 SASL 选项。选项用于修改 libsasl(3LIB) 和相关插件的行为。可由客户机或服务器使用。

SASL_CB_LOG

设置 libsasl 及其插件的日志记录函数。缺省行为是使用 syslog

SASL_CB_GETPATH

获取以冒号分隔的 SASL 插件搜索路径列表。缺省路径取决于以下体系结构:

  • 32 位 SPARC 体系结构: /usr/lib/sasl

  • 32 位 x86 体系结构: /usr/lib/sasl

  • 64 位 SPARC 体系结构: /usr/lib/sasl/sparcv9

  • x64 体系结构: /usr/lib/sasl/amd64

SASL_CB_GETCONF

获取 SASL 服务器的配置目录的路径。缺省设置为 /etc/sasl

SASL_CB_LANGUAGE

为客户机和服务器错误消息和客户机提示指定以逗号分隔的 RFC 1766 语言代码列表(按优先级顺序)。缺省设置为 i-default

SASL_CB_VERIFYFILE

验证配置文件和插件文件。

SASL 提供以下仅限客户机使用的回调:

SASL_CB_USER

获取客户机用户名。用户名与授权 ID 相同。LOGNAME 环境变量为缺省设置。

SASL_CB_AUTHNAME

获取客户机验证名称。

SASL_CB_PASS

获取基于客户机口令短语的机密。

SASL_CB_ECHOPROMPT

获取给定质询提示的结果。可以回显来自客户机的输入。

SASL_CB_NOECHOPROMPT

获取给定质询提示的结果。不应回显来自客户机的输入。

SASL_CB_GETREALM

设置用于验证的领域。

SASL 提供以下仅限服务器使用的回调:

SASL_CB_PROXY_POLICY

检查是否授权经过验证的用户代表指定用户执行操作。如果未注册此回调,则经过验证的用户与要授权的用户必须是同一用户。如果这些 ID 不同,则验证将失败。请使用服务器应用程序来维护非标准授权策略。

SASL_CB_SERVER_USERDB_CHECKPASS

针对调用方提供的用户数据库验证纯文本口令。

SASL_CB_SERVER_USERDB_SETPASS

在用户数据库中存储纯文本口令

SASL_CB_CANON_USER

调用应用程序提供的用户标准化函数。

首次初始化 SASL 库时,服务器和客户机会声明所有必要的全局回调。执行 SASL 会话之前或期间可以使用全局回调。初始化之前,回调将执行诸如装入插件、记录数据和读取配置文件之类的任务。SASL 会话开始时,可以声明其他回调。这类回调可以覆盖全局回调(如果必要)。

SASL 连接上下文

libsasl 使用 SASL 连接上下文维护 SASL 客户机和 SASL 服务器的每个 SASL 会话的状态。每个上下文一次只能用于一个验证和安全会话。维护的状态包括以下信息:

SASL 周期中的步骤

下图显示了 SASL 生命周期中的步骤。客户机操作显示在图的左侧,服务器操作显示在右侧。中间的箭头显示客户机与服务器之间通过外部连接执行的交互操作。

图 7–2 SASL 生命周期

图中显示了 SASL 生命周期中与客户机和服务器对应的各阶段。

以下各节说明了生命周期中的步骤。

libsasl 初始化

客户机调用 sasl_client_init() 来初始化 libsasl 以供客户机使用;而服务器调用 sasl_server_init() 来初始化 libsasl 以供服务器使用。

运行 sasl_client_init() 时,将装入 SASL 客户机、该客户机的机制以及该客户机的标准化插件。同样,调用 sasl_server_init() 时,将装入 SASL 服务器、该服务器的机制、该服务器的标准化插件以及该服务器的 auxprop 插件。调用 sasl_client_init() 后,可以使用 sasl_client_add_plugin()sasl_canonuser_add_plugin() 来添加其他客户机插件。在服务器端,调用 sasl_server_init() 后,可以通过 sasl_server_add_plugin()sasl_canonuser_add_plugin()sasl_auxprop_add_plugin() 来添加其他的服务器插件。依据体系结构,我们在 Solaris 软件的以下目录中提供了 SASL 机制:

可以使用 SASL_CB_GETPATH 回调覆盖缺省位置。

此时,可以设置所有必需的全局回调。SASL 客户机和服务器可能包括以下回调:

此外,SASL 服务器还可能包括 SASL_CB_GETCONF 回调。

SASL 会话初始化

服务器和客户机通过协议建立连接。要使用 SASL 执行验证,服务器和客户机可以分别使用 sasl_server_new()sasl_client_new() 来创建 SASL 连接上下文。SASL 客户机和服务器可以使用 sasl_setprop() 来设置对机制强制执行安全限制的属性。此方法使 SASL 消费方应用程序可以决定指定 SASL 连接上下文的最小 SSF、最大 SSF 和安全属性。

#define SASL_SEC_NOPLAINTEXT            0x0001

#define SASL_SEC_NOACTIVE               0x0002

#define SASL_SEC_NODICTIONARY           0x0004

#define SASL_SEC_FORWARD_SECRECY        0x0008

#define SASL_SEC_NOANONYMOUS            0x0010

#define SASL_SEC_PASS_CREDENTIALS       0x0020

#define SASL_SEC_MUTUAL_AUTH            0x0040

注 –

验证和安全层可由客户机/服务器协议提供,也可由 libsasl 外部的某些其他机制提供。在这类情况下,可以使用 sasl_setprop() 来设置外部验证 ID 或外部 SSF。例如,请考虑协议对服务器结合使用 SSL 和客户机验证的情况。在这种情况下,外部验证标识可以为客户机的主题名称。外部 SSF 可以为密钥大小。


对于服务器,libsasl 可以依据安全属性和外部 SSF 来确定可用的 SASL 机制。客户机可以通过协议从 SASL 服务器获取可用的 SASL 机制。

为使 SASL 服务器可以创建 SASL 连接上下文,服务器应该调用 sasl_server_new()。可以重复使用不再使用的现有 SASL 连接上下文。但是,可能需要重置以下参数:

#define SASL_DEFUSERREALM 3     /* default realm passed to server_new or set with setprop */

#define SASL_IPLOCALPORT 8      /* iplocalport string passed to server_new */

#define SASL_IPREMOTEPORT 9     /* ipremoteport string passed to server_new */

#define SASL_SERVICE    12      /* service passed to sasl_*_new */

#define SASL_SERVERFQDN 13      /* serverFQDN passed to sasl_*_new */

可以修改 sasl_client_new()sasl_server_new() 的任何参数,但回调和协议标志除外。

服务器和客户机还可以使用 sasl_setprop() 来指定以下属性,从而建立安全策略并设置连接特定参数:

#define SASL_SSF_EXTERNAL 100 /* external SSF active (sasl_ssf_t *) */

#define SASL_SEC_PROPS 101 /* sasl_security_properties_t */

#define SASL_AUTH_EXTERNAL 102 /* external authentication ID (const char *)

 */

服务器可以调用 sasl_listmech() 来获取满足安全策略的可用 SASL 机制的列表。一般情况下,客户机可以采用与协议相关的方式从服务器获取可用机制的列表。

下图说明了 SASL 会话的初始化过程。在此图和后续的图中,为了简单起见,省略了通过协议进行传输后的数据检查。

图 7–3 SASL 会话初始化

图中显示了在 SASL 会话初始化期间客户机和服务器执行的步骤。

SASL 验证

验证根据使用的安全机制采用不同数量的客户机和服务器步骤。SASL 客户机将调用 sasl_client_start() 和要使用的安全机制列表。此列表通常来自服务器。libsasl 将依据可用机制和客户机的安全策略选择要用于此 SASL 会话的最佳机制。客户机的安全策略控制允许的机制。选定的机制由 sasl_client_start() 返回。有时,客户机的安全机制需要其他验证信息。对于已注册的回调,libsasl 将调用指定的回调,除非回调函数为 NULL。如果回调函数为 NULL,则 libsasl 将返回 SASL_INTERACT 和对所需信息的请求。如果返回 SASL_INTERACT,则应使用请求的信息调用 sasl_client_start()

如果 sasl_client_start() 返回 SASL_CONTINUE 或 SASL_OK,则客户机应向服务器发送包含生成的所有验证数据的选定机制。如果返回任何其他值,则表示出现了错误。例如,任何机制可能都不可用。

服务器将接收客户机选定的机制,以及所有的验证数据。随后,服务器将调用 sasl_server_start() 来初始化此会话的机制数据。sasl_server_start() 还将处理所有的验证数据。如果 sasl_server_start() 返回 SASL_CONTINUE 或 SASL_OK,则服务器将发送验证数据。如果 sasl_server_start() 返回任何其他值,则表示出现了错误,如不可接受的机制或验证失败。必须中止验证。应该释放或重复使用 SASL 上下文。

下图说明了此部分的验证过程。

图 7–4 SASL 验证:发送客户机数据

图中显示客户机向服务器发送验证数据时客户机和服务器执行的步骤。

如果服务器对 sasl_server_start() 的调用返回 SASL_CONTINUE,则服务器将继续与客户机进行通信,以获取所有必要的验证信息。后续步骤的数目取决于机制。如果需要,客户机可以调用 sasl_client_step() 来处理来自服务器的验证数据并生成回复。同样,服务器可以调用 sasl_server_step() 来处理来自客户机的验证并相应地生成回复。此交换将持续进行下去,直到验证完成或出现错误为止。如果返回 SASL_OK,则指示对客户机或服务器的验证已成功完成。SASL 机制可能仍然具有要发送给另一端的其他数据,因此另一端可以完成验证。在两端都完成验证后,服务器和客户机便可查询彼此的属性。

下图显示了服务器与客户机之间为传输其他验证数据所执行的交互。

图 7–5 SASL 验证:处理服务器数据

图中显示服务器向客户机返回数据时客户机和服务器执行的步骤。

SASL 保密性和完整性

要检查安全层,请使用 sasl_getprop(3SASL) 函数来查看安全强度因子 (security strength factor, SSF) 的值是否大于 0。如果已协商安全层,则成功验证后客户机和服务器必须使用生成的 SSF。在客户机与服务器之间交换数据的方式与验证的方式相同。通过协议向客户机或服务器发送数据之前,将 sasl_encode() 应用于数据。在接收端,使用 sasl_decode() 对数据进行解码。如果未协商安全层,则无需 SASL 连接上下文。随后,即可处理或重复使用该上下文。

释放 SASL 会话

只有在不会重复使用会话时,才应释放 SASL 连接上下文。sasl_dispose() 将释放 SASL 连接上下文以及所有关联的资源和机制。调用 sasl_done() 之前,必须处理 SASL 连接上下文。sasl_done() 不负责释放 SASL 连接的上下文资源。请参见libsasl 清除

释放 SASL 会话时,将通知关联的机制所有状态均可释放。只有在不会重复使用 SASL 会话时,才应释放该会话。否则,其他会话可以重复使用 SASL 状态。客户机和服务器都使用 sasl_dispose() 释放 SASL 连接上下文。

libsasl 清除

此步骤可释放 SASL 库和插件中的所有资源。客户机和服务器可以调用 sasl_done() 来释放 libsasl() 资源并卸载所有的 SASL 插件。sasl_done() 不会释放 SASL 连接上下文。请注意,如果应用程序同时为 SASL 客户机和 SASL 服务器,则 sasl_done() 将同时释放 SASL 客户机和 SASL 服务器资源。您不能仅释放客户机或服务器的资源。


注意 – 注意 –

库不应调用 sasl_done()。应用程序在调用 sasl_done() 时务必要谨慎,避免与所有可能使用 libsasl 的库发生干扰。