sasl认证原理

 SASL - 简单认证和安全层 

     SASL是一种用来扩充C/S模式验证能力的机制认证机制,  全称Simple Authentication and Security Layer.

     当你设定sasl时,你必须决定两件事;一是用于交换“标识信 息”(或称身份证书)的验证机制;一是决定标识信息存储方法的验证架构。

     sasl验证机制规范client与server之间的应答过程以及传输内容的编码方法,sasl验证架构决定服务器本身如何存储客户端的身份证书以及如何核验客户端提供的密码。

     如果客户端能成功通过验证,服务器端就能确定用户的身份, 并借此决定用户具有怎样的权限。

 比较常见的机制;

4.1 plain(较常用)

   plain是最简单的机制,但同时也是最危险的机制,因为身份证书(登录名称与密码)是以base64字符串格式通过网络,没有任何加密保护措施。因此,使用plain机制时,你可能会想要结合tls。

4.2 login

   login不是其正式支持的机制,但某些旧版的mua使用这种机制,所以cyrus sasl让你可选择其是否支持login机制。如果你的用户仍在使用这类老掉牙的mua,你必须在编译sasl函数库时,指定要包含login的支持。 login的证书交换过程类似plain。

4.3 otp

otp是一种使用“单次密码”的验证机制。此机制不提供任何加密保护,因为没必要--每个密码都只能使用一次,每次联机都要改用新密码。smto client必须能够产生otp证书。

4.4 digest-md5(较常用)

   使用这种机制时,client与server共享同一个隐性密码,而且此密码不通过网络传输。验证过程是从服务器先提出challenge(质询)开始, 客户端使用此challenge与隐性密码计算出一个response(应答)。不同的challenge,不可能计算出相同的response;任何拥 有secret password的一方,都可以用相同的challenge算出相同的response。因此,服务器只要比较客户端返回的response是否与自己算 出的response相同,就可以知道客户端所拥有的密码是否正确。由于真正的密码并没有通过网络,所以不怕网络监测。

4.5 kerberos

   kerberos是一种网络型验证协议。除非你的网络已经使用kerberos,否则你应该用不到kerberos机制;相对的,如果你的网络已经架设了kerberos验证中心,sasl就能完美的将smtp验证整合进现有的体系。

4.6 anonymous

   anonymous机制对smtp没有意义,因为smtp验证的用意在于限制转发服务的使用对象,而不是为了形成open relay,sasl之所以提供这种机制,主要是为了支持其他协议。
当 客户端链接到一个支持sasl的邮件服务器时,服务器会以优先级列出可用的机制供客户端选择。如果客户端也支持多钟机制,则当第一种机制验证失败时,客户 端可能会继续尝试第二种机制,直到通过验证或是所有机制都失败为止。如果双方在一开始就无法协调出共同的机制,验证过程就算失败。
一旦双方在使用哪种机制上达成共识,就开始进行验证过程。实际的交互过程随机制而定,但通常包含一次或多次应答过程。验证协议本身也规定了应答内容的编码格式。

  5. 总结 

       数字证书, 是级联认证派发的, 最上层是根CA认证中心. 数字证书的根本作用, 是为了保证所有人公钥的安全性和真实性. 大致认证过程是: 通过CA的公钥来解出该CA所派发的证书里面所包含的公钥(用户或者机构的). 并通过该公钥来验证证书持有人的真实性. (因为持有人并不一定是证书所有人)

       通过上面对SSL的分析,我们可以看到,SSL并不能阻止别人获得你传输的数据,但是由于你传输的数据都是加密过的,别人拿到了毫无用处,一样可以保护信 息的安全。还有一点需要强调一下,SSL并不依赖于TCP,它可以建立在任何可靠的传输层协议(比如TCP)之上。也就是说SSL是不能建立在UDP之上 的。这是显然的,如果传输都不可靠,偶尔丢两个包或者包的顺序换一换的话,怎么保证安全呢?

       SASL是提供一种用户身份认证机制, 你可以简单认为是用来认证用户的账号/密码是否运行进入系统或者使用系统的服务. 一般较长使用digest-md5, 该种机制下, 密码可以不用在网络上传输, 也就不用怕密码被窃听.


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) 用于检查是否允许用户使用特定选项。

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

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

SASL 体系结构

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

图 1 SASL 体系结构

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

安全机制

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

  • 在客户端进行验证

  • 在服务器端进行验证

  • 完整性,即检查传输的数据是否保持不变

  • 保密性,即对传输的数据进行加密和解密

SASL 安全强度因子

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

  • 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。

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 SSF 的安全属性

  • 验证状态以及安全层信息

SASL 周期中的步骤

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

图 2 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 机制:

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

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

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

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

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

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

  • SASL_CB_GETOPT

  • SASL_CB_LOG

  • SASL_CB_GETPATH

  • SASL_CB_VERIFYFILE

此外,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_SSF_EXTERNAL-用于设置强度因子,即密钥中的位数

  • SASL_SEC_PROPS-用于定义安全策略

  • SASL_AUTH_EXTERNAL-外部验证 ID

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

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

图 3 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 上下文。

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

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


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

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

图 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 的库发生干扰。


SASL 示例

本节说明客户机应用程序与服务器应用程序之间的典型 SASL 会话。该示例包含以下步骤:

  1. 客户机应用程序可以初始化 libsasl 并设置以下全局回调:

    • SASL_CB_GETREALM

    • SASL_CB_USER

    • SASL_CB_AUTHNAME

    • SASL_CB_PASS

    • SASL_CB_GETPATH

    • SASL_CB_LIST_END

  2. 服务器应用程序可以初始化 libsasl 并设置以下全局回调:

    • SASL_CB_LOG

    • SASL_CB_LIST_END

  3. 客户机将创建 SASL 连接上下文,设置安全属性并从服务器请求可用机制的列表。

  4. 服务器将创建 SASL 连接上下文,设置安全属性,获取适当 SASL 机制的列表并向客户机发送该列表。

  5. 客户机将接收可用机制的列表,选择一种机制,并向服务器发送所选择的机制以及所有验证数据。

  6. 随后,客户机和服务器将交换 SASL 数据,直到验证和安全层协商完成为止。

  7. 验证完成后,客户机和服务器将确定是否已协商安全层。客户机将对测试消息进行编码。然后,会将该消息发送给服务器。服务器也会确定经过验证的用户的用户名和该用户的领域。

  8. 服务器将接收、解码和列显编码的消息。

  9. 客户机将调用 sasl_dispose() 以释放客户机的 SASL 连接上下文。随后,客户机将调用 sasl_done() 以释放 libsasl 资源。

  10. 服务器将调用 sasl_dispose() 以释放客户机连接上下文。

以下是客户机与服务器之间的对话。执行调用时,会显示对 libsasl 的每个调用。每次数据传输都由发送者和接收者指明。数据采用编码的形式显示,并在前面加上表示来源的字符: C: 表示来自于客户机,S: 表示来自于服务器。附录 D,SASL 示例的源代码 中同时提供了两种应用程序的源代码。

客户机

 
% doc-sample-client

*** Calling sasl_client_init() to initialize libsasl for client use ***

*** Calling sasl_client_new() to create client SASL connection context ***

*** Calling sasl_setprop() to set sasl context security properties ***

Waiting for mechanism list from server...

服务器

 
% doc-sample-server digest-md5

*** Calling sasl_server_init() to initialize libsasl for server use ***

*** Calling sasl_server_new() to create server SASL connection context ***

*** Calling sasl_setprop() to set sasl context security properties ***

Forcing use of mechanism digest-md5

Sending list of 1 mechanism(s)

S: ZGlnZXN0LW1kNQ==

客户机

 
S: ZGlnZXN0LW1kNQ==

received 10 byte message

got 'digest-md5'

Choosing best mechanism from: digest-md5

*** Calling sasl_client_start() ***

Using mechanism DIGEST-MD5

Sending initial response...

C: RElHRVNULU1ENQ==

Waiting for server reply...

服务器

 
C: RElHRVNULU1ENQ==

got 'DIGEST-MD5'

*** Calling sasl_server_start() ***

Sending response...

S: bm9uY2U9IklicGxhRHJZNE4Z1gyVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM\

sbT0iam0xMTQxNDIiLHFvcD0iYXV0aCxhdXRoLWludCxhdXRoLWNvbmYiLGNpcGhlcj0ic\

QwLHJjNC01NixyYzQiLG1heGJ1Zj0yMDQ4LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1k\

XNz

Waiting for client reply...

客户机

 
S: bm9uY2U9IklicGxhRHJZNE4Z1gyVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM\

sbT0iam0xMTQxNDIiLHFvcD0iYXV0aCxhdXRoLWludCxhdXRoLWNvbmYiLGNpcGhlcj0ic\

QwLHJjNC01NixyYzQiLG1heGJ1Zj0yMDQ4LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1k\

XNz

received 171 byte message

got 'nonce="IbplaDrY4N4szhgX2VneC9y16NalT9W/ju+rjybdjhs=",\

realm="jm114142",qop="auth,auth-int,auth-conf",cipher="rc4-40,rc4-56,\

rc4",maxbuf=2048,charset=utf-8,algorithm=md5-sess'

*** Calling sasl_client_step() ***

Please enter your authorization name : zzzz

Please enter your authentication name : zzzz

Please enter your password : zz

*** Calling sasl_client_step() ***

Sending response...

C: dXNlcm5hbWU9Inp6enoiLHJlYWxtPSJqbTExNDE0MiIsbm9uY2U9IklicGxhRHJZNE4\

yVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM9Iixjbm9uY2U9InlqZ2hMVmhjRFJMa0Fob\

tDS0p2WVUxMUM4V1NycjJVWm5IR2Vkclk9IixuYz0wMDAwMDAwMSxxb3A9YXV0aC1jb25m\

Ghlcj0icmM0IixtYXhidWY9MjA0OCxkaWdlc3QtdXJpPSJyY21kLyIscmVzcG9uc2U9OTY\

ODI1MmRmNzY4YTJjYzkxYjJjZDMyYTk0ZWM=

Waiting for server reply...

服务器

 
C: dXNlcm5hbWU9Inp6enoiLHJlYWxtPSJqbTExNDE0MiIsbm9uY2U9IklicGxhRHJZNE4\

yVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM9Iixjbm9uY2U9InlqZ2hMVmhjRFJMa0Fob\

tDS0p2WVUxMUM4V1NycjJVWm5IR2Vkclk9IixuYz0wMDAwMDAwMSxxb3A9YXV0aC1jb25m\

Ghlcj0icmM0IixtYXhidWY9MjA0OCxkaWdlc3QtdXJpPSJyY21kLyIscmVzcG9uc2U9OTY\

ODI1MmRmNzY4YTJjYzkxYjJjZDMyYTk0ZWM=

got 'username="zzzz",realm="jm114142",\

nonce="IbplaDrY4N4szhgX2VneC9y16NalT9W/ju+rjybdjhs=",\

cnonce="yjghLVhcDRLkAhoirwKCKJvYU11C8WSrr2UZnHGedrY=", \

nc=00000001,qop=auth-conf,cipher="rc4",maxbuf=2048,digest-uri="rcmd/",\

response=966e978252df768a2cc91b2cd32a94ec'

*** Calling sasl_server_step() ***

Sending response...

S: cnNwYXV0aD0yYjEzMzRjYzU4NTE4MTEwOWM3OTdhMjUwYjkwMzk3OQ==

Waiting for client reply...

客户机

 
S: cnNwYXV0aD0yYjEzMzRjYzU4NTE4MTEwOWM3OTdhMjUwYjkwMzk3OQ==

received 40 byte message

got 'rspauth=2b1334cc585181109c797a250b903979'

*** Calling sasl_client_step() ***

C:

Negotiation complete

*** Calling sasl_getprop() ***

Username: zzzz

SSF: 128

Waiting for encoded message...

服务器

 
Waiting for client reply... 

C: got '' *** Calling sasl_server_step() *** 

Negotiation complete 

*** Calling sasl_getprop() to get username, realm, ssf *** 

Username: zzzz 

Realm: 22c38 

SSF: 128 

*** Calling sasl_encode() *** sending encrypted message 'srv message 1'

S: AAAAHvArjnAvDFuMBqAAxkqdumzJB6VD1oajiwABAAAAAA==

客户机

 
S: AAAAHvArjnAvDFuMBqAAxkqdumzJB6VD1oajiwABAAAAAA==

received 34 byte message

got ''

*** Calling sasl_decode() ***

received decoded message 'srv message 1'

*** Calling sasl_encode() ***

sending encrypted message 'client message 1'

C: AAAAIRdkTEMYOn9X4NXkxPc3OTFvAZUnLbZANqzn6gABAAAAAA==

*** Calling sasl_dispose() to release client SASL connection context ***

*** Calling sasl_done() to release libsasl resources ***

服务器

 
Waiting for encrypted message...

C: AAAAIRdkTEMYOn9X4NXkxPc3OTFvAZUnLbZANqzn6gABAAAAAA==

got ''

*** Calling sasl_decode() ***

received decoded message 'client message 1'

*** Calling sasl_dispose() to release client SASL connection context ***

服务提供者的 SASL

本节介绍如何创建用于为 SASL 应用程序提供机制和其他服务的插件。


注 –

由于导出规程,对于非 Solaris 客户机/服务器机制插件,Solaris SASL SPI 不支持安全层。 因此,非 Solaris 客户机/服务器机制插件不能提供完整性和保密性服务。Solaris 客户机/服务器机制插件没有此限制。


SASL 插件概述

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 插件的重要结构

libsasl 与插件之间的通信是通过以下结构完成的:

  • 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) 插件

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 插件开发指南

本节提供一些用于开发 SASL 插件的其他建议。

SASL 插件中的错误报告

适当的错误报告可以帮助跟踪验证问题和其他调试问题。建议插件开发者使用 sasl_utils_t 结构中的 sasl_seterror() 回调为给定连接提供详细的错误信息。

SASL 插件中的内存分配

在 SASL 中分配内存的一般规则是在不再需要已分配的任何内存时将其释放。遵循此规则可以提高性能和可移植性,而且可以避免内存泄漏。

设置 SASL 协商顺序

插件机制可以设置客户机和服务器通过以下标志执行 SASL 会话的顺序:

  • SASL_FEAT_WANT_CLIENT_FIRST-客户端将开始交换。

  • SASL_FEAT_WANT_SERVER_LAST-服务器将最终数据发送到客户机。

如果未设置任何标志,则机制插件将在内部设置顺序。在这种情况下,机制必须同时检查客户机和服务器中需要发送的数据。请注意,只有在协议允许初始响应时,才有可能出现客户机首先发送的情况。

服务器最后发送的情况要求在步骤函数返回 SASL_OK 时插件可设置 *serverout。永远不让服务器最后发送的那些机制必须将 *serverout 设置为 NULL。始终让服务器最后发送的那些机制需要将 *serverout 指向成功数据

发布了544 篇原创文章 · 获赞 633 · 访问量 116万+

猜你喜欢

转载自blog.csdn.net/asdfsadfasdfsa/article/details/104526328