Solaris 开发者安全性指南

开发使用 GSS-API 的应用程序

本节说明如何使用 GSS-API 来实现安全的数据交换。 本节将重点介绍那些对于使用 GSS-API 至关重要的函数。 有关更多信息,请参见附录 B,GSS-API 参考,其中包含所有 GSS-API 函数、状态码和数据类型的列表。 要查找有关任何 GSS-API 函数的更多信息,请检查相应的手册页。

本手册中的示例遵循一个简单的模型。 客户机应用程序将数据直接发送到远程服务器, 而无需通过传输协议层(如 RPC)进行中间调用。

GSS-API 的一般用法

下面是使用 GSS-API 的一般步骤:

  1. 每个应用程序(无论是发送者还是接受器)都明确获取凭证,除非已经自动获取凭证。

  2. 发送者启动一个安全上下文, 接受器接受该上下文。

  3. 发送者向要传送的数据应用安全保护机制。 发送者会对消息进行加密或者使用标识标记对数据进行标记。 发送者随后将传送受保护的消息。


    注 –

    发送者可以选择不应用安全保护机制,在这种情况下,消息仅具有缺省的 GSS-API 安全服务,即验证。


  4. 接受器根据需要对消息进行解密,并在适当的情况下对消息进行验证。

  5. (可选)接受器将标识标记返回到发送者进行确认。

  6. 这两个应用程序都会销毁共享的安全上下文。 如有必要,分配功能还可以解除分配其余任何 GSS-API 数据。


注意 – 注意 –

调用应用程序负责释放已经分配的所有数据空间。


使用 GSS-API 的应用程序需要包含头文件 gssapi.h

在 GSS-API 中使用凭证

凭证是指向主体名称提供应用程序声明证明的数据结构。 应用程序使用凭证来建立其全局标识。 此外,凭证还可用于确认实体的权限。

GSS-API 不提供凭证。 凭证由那些以 GSS-API 为基础的安全机制在调用 GSS-API 函数之前创建。 在许多情况下,用户会在登录时接收凭证。

指定的 GSS-API 凭证对于单个主体有效。 单个凭证可以包含该主体的多个元素,每个元素由不同的机制创建。 如果某个凭证是在包含多个安全机制的计算机上获取的,则该凭证在传输到包含其中部分机制的计算机上时有效。 GSS-API 通过 gss_cred_id_t 结构访问凭证。 此结构称为凭证句柄。 凭证对于应用程序是不透明的。 因此,应用程序无需知道指定凭证的具体信息。

凭证采用以下三种形式:

在 GSS-API 中获取凭证

服务器和客户机都必须获取各自的凭证,然后才能建立安全上下文。 应用程序可以在凭证到期之前重用它,在到期之后必须重新获取凭证。 客户机使用的凭证与由服务器使用的凭证可以具有不同的生命周期。

基于 GSS-API 的应用程序可以通过以下两种方法获取凭证:

大多数情况下,仅有上下文接受器(即服务器)才能调用 gss_acquire_cred()。 上下文启动器(即客户机)通常在登录时接收凭证。 因此,客户机通常会指定缺省凭证。 服务器也可以跳过 gss_acquire_cred() 而使用其缺省凭证。

客户机的凭证用于向其他进程证明该客户机的身份。 服务器在获取凭证后即可接受安全上下文。 因此,如果某个客户机向服务器发出 ftp 请求,则表明该客户机可能已经在登录时获取了凭证。 客户机尝试启动上下文时,GSS-API 会自动检索凭证。 但是,服务器程序可以明确获取所请求服务 (ftp) 的凭证。

如果 gss_acquire_cred() 成功完成,则将返回 GSS_S_COMPLETE。 如果不能返回有效的凭证,则将返回 GSS_S_NO_CRED。 有关其他错误代码,请参见 gss_acquire_cred(3GSS) 手册页。 有关相应的示例,请参见第 8 章中的“获取凭证”。

gss_add_cred()gss_acquire_cred() 相似。 但是,gss_add_cred() 允许应用程序使用现有的凭证来创建新句柄或者添加新的凭证元素。 如果将 GSS_C_NO_CREDENTIAL 指定为现有的凭证,则 gss_add_cred() 会根据缺省行为创建新凭证。 有关更多信息,请参见 gss_add_cred(3GSS) 手册页。

在 GSS-API 中使用上下文

在提供安全性方面,GSS-API 最重要的两个任务就是创建安全上下文和保护数据。 应用程序获取必要的凭证之后,必须建立安全上下文。 建立上下文时会涉及到两个应用程序,一个应用程序(通常是客户机)用于启动上下文,另一个应用程序(通常是服务器)用于接受上下文。 允许在对等应用程序之间使用多个上下文。

相互通信的应用程序通过交换验证令牌来建立共同的安全上下文。 安全上下文是一对 GSS-API 数据结构,其中包含要在两个应用程序之间共享的信息。 这些信息从安全的角度描述了每个应用程序的状态。 安全上下文是保护数据所必需的。

在 GSS-API 中启动上下文

gss_init_sec_context() 函数用于启动应用程序和远程服务器之间的安全上下文。 如果成功,该函数将返回一个上下文句柄以建立上下文,还将返回一个要发送到接受器的上下文级别的令牌。 调用 gss_init_sec_context() 之前,客户机应当执行以下任务:

  1. 使用 gss_acquire_cred() 获取凭证(如有必要)。 通常,客户机会在登录时接收凭证。gss_acquire_cred() 只能从正在运行的操作系统中检索初始凭证。

  2. 使用 gss_import_name() 以 GSS-API 内部格式导入服务器的名称。 有关名称和 gss_import_name() 的更多信息,请参见GSS-API 中的名称

调用 gss_init_sec_context() 时,客户机通常会传递以下参数值:

应用程序不一定要使用这些缺省值。 此外,客户机还可以使用 req_flags 参数指定其他安全参数的要求。 下面描述了完整的 gss_init_sec_context() 参数集。

上下文接受器可能需要多次握手才能建立上下文, 也即是说,接受器会要求启动器先发送多段上下文信息,然后再建立完整的上下文。 因此,为了实现可移植性,应始终在检查上下文是否已完全建立的循环过程中启动上下文。

如果上下文不完整,则 gss_init_sec_context() 将返回一个主状态码 GSS_C_CONTINUE_NEEDED。 因此,循环应当使用 gss_init_sec_context() 的返回值来测试是否继续执行启动循环操作。

客户机将上下文信息以输出令牌形式传递到服务器,输出令牌通过 gss_init_sec_context() 返回。 客户机可接收从服务器返回的输入令牌形式的信息。 以后调用 gss_init_sec_context() 时,输入令牌可以作为参数传递。 但是,如果所收到的输入令牌的长度为零,则表明服务器不再需要其他输出令牌。

因此,除了检查 gss_init_sec_context() 的返回状态以外,该循环还应当检查输入令牌的长度。 如果长度是非零值,则需要向服务器发送其他令牌。 开始循环之前,应将输入令牌的长度初始化为零。 请将输入令牌设置为 GSS_C_NO_BUFFER 或者将结构的长度字段设置为零值。

下面的伪代码举例说明如何从客户端建立上下文:

context = GSS_C_NO_CONTEXT

input token = GSS_C_NO_BUFFER



do



     call gss_init_sec_context(credential, context, name, input token, 

                                           output token, other args...)



     if (there's an output token to send to the acceptor)

          send the output token to the acceptor

          release the output token



     if (the context is not complete)

          receive an input token from the acceptor



     if (there's a GSS-API error)

          delete the context



until the context is complete

错误检查范围越大,实际循环将越完整。 有关此类上下文启动循环的实际示例,请参见建立与服务器的安全上下文。 此外,gss_init_sec_context(3GSS) 手册页还提供了比较特殊的示例。

通常,在上下文未完全建立时返回的参数值即是那些会在上下文完整建立时返回的值。 有关更多信息,请参见 gss_init_sec_context() 手册页。

如果 gss_init_sec_context() 成功完成,则将返回 GSS_S_COMPLETE。 如果对等应用程序需要上下文建立令牌,则将返回 GSS_S_CONTINUE_NEEDED。 如果出现错误,则将返回 gss_init_sec_context(3GSS) 手册页中所示的错误代码。

如果上下文启动失败,则客户机会断开与服务器的连接。

在 GSS-API 中接受上下文

建立上下文的另一方面是接受上下文,这可通过 gss_accept_sec_context() 函数来完成。 通常情况下,服务器接受客户机使用 gss_init_sec_context() 已启动的上下文。

gss_accept_sec_context() 的主要输入是来自启动器的输入令牌。 启动器会返回一个上下文句柄,以及一个要返回到启动器的输出令牌。 但是,服务器应当首先获取客户机所请求服务的凭证,然后才能调用 gss_accept_sec_context()。 服务器使用 gss_acquire_cred() 函数获取这些凭证。 或者,服务器可以指定在调用 gss_accept_sec_context() 时的缺省凭证(即 GSS_C_NO_CREDENTIAL),从而无需明确获取凭证。

调用 gss_accept_sec_context() 时,服务器可以按如下方式设置以下参数:

以下几段将介绍完整的 gss_accept_sec_context() 参数集。

建立安全上下文可能需要几次握手。 启动器和接受器通常需要先发送多段上下文信息,然后才能建立完整的上下文。 因此,为了实现可移植性,应始终在检查上下文是否已完全建立的循环过程中接受上下文。 如果上下文尚未建立,则 gss_accept_sec_context() 将返回一个主状态码 GSS_C_CONTINUE_NEEDED。 因此,循环应当使用 gss_accept_sec_context() 的返回值来测试是否继续执行接受循环操作。

上下文接受器将上下文信息以输出令牌形式返回到启动器,输出令牌通过 gss_accept_sec_context() 返回。 以后,接受器可以从启动器接收输入令牌形式的其他信息。 以后调用 gss_accept_sec_context() 时,输入令牌将作为参数传递。 gss_accept_sec_context() 不再向启动器发送令牌时,将返回一个长度为零的输出令牌。 除了检查返回状态 gss_accept_sec_context() 以外,循环还应当检查输出令牌的长度,查看是否必须发送其他令牌。 开始循环之前,应将输出令牌的长度初始化为零。 请将输出令牌设置为 GSS_C_NO_BUFFER 或者将结构的长度字段设置为零值。

下面的伪代码举例说明如何从服务器端建立上下文:

context = GSS_C_NO_CONTEXT

output token = GSS_C_NO_BUFFER



do



     receive an input token from the initiator



     call gss_accept_sec_context(context, cred handle, input token,

                                                output token, other args...)



     if (there's an output token to send to the initiator)

          send the output token to the initiator

          release the output token



     if (there's a GSS-API error)

          delete the context



until the context is complete

错误检查范围越大,实际循环将越完整。有关此类上下文接受循环的实际示例,请参见建立与服务器的安全上下文。此外,gss_accept_sec_context() 手册页还提供了一个示例。

同样,GSS-API 不会收发令牌,令牌必须由应用程序进行处理。有关令牌传送函数的示例可以在各种 GSS-API 样例函数中找到。

gss_accept_sec_context() 在成功完成时将返回 GSS_S_COMPLETE。如果上下文不完整,该函数将返回 GSS_S_CONTINUE_NEEDED。如果出现错误,该函数将返回错误代码。有关更多信息,请参见 gss_accept_sec_context(3GSS) 手册页。

在 GSS-API 中使用其他上下文服务

gss_init_sec_context() 函数允许应用程序请求超出所建立基本上下文范围的其他数据保护服务。可通过 gss_init_sec_context()req_flags 参数来请求这些服务。

并非所有的机制都提供所有这些服务。gss_init_sec_context()ret_flags 参数用于指示指定上下文中可用的服务。同样,上下文接受器也会通过检查 gss_accept_sec_context() 返回的 ret_flags 值来确定可用的服务。这些服务将在以下几节中进行介绍。

在 GSS-API 中授予凭证

如果允许的话,上下文启动器可以请求由上下文接受器充当代理。在此类情况下,接受器可以代表启动器启动进一步的上下文。

假设计算机 A 上的某个用户希望通过 rlogin 登录到计算机 B,然后通过 rlogin 从计算机 B 登录到计算机 C。根据所使用的机制,所授予的凭证会将 B 标识为 A,或者将 B 标识为 A 的代理。

如果允许授予的话,可以将 ret_flags 设置为 GSS_C_DELEG_FLAG。接受器可接收所授予的凭证并将其作为 gss_accept_sec_context()delegated_cred_handle 参数。授予凭证不同于导出上下文。请参见在 GSS-API 中导出和导入上下文。二者的一个区别就是应用程序可以将其凭证同时授予多次,而上下文一次只能由一个进程持有。

在 GSS-API 中的对等应用程序之间执行相互验证

将文件传送到 ftp 站点的用户通常无需站点标识的证明。另一方面,需要向应用程序提供信用卡号的用户则需要接收者标识的确切证明。在此类情况下,需要进行相互验证。上下文启动器和上下文接受器都需要证明各自的标识。

上下文启动器可以通过将 gss_init_sec_context() req_flags 参数设置为值 GSS_C_MUTUAL_FLAG 来请求相互验证。如果已经对相互验证进行了授权,则函数会通过将 ret_flags 参数设置为该值来表示授权。如果所请求的相互验证不可用,则启动应用程序会负责进行相应的响应,在此情况下 GSS-API 不会自动终止上下文。另外,即使没有特定的请求,某些机制也会始终执行相互验证。

在 GSS-API 中执行匿名验证

正常使用 GSS-API 时,启动器的标识会在建立上下文的过程中提供给接受器。 但是,上下文启动器会请求不将其标识显示给上下文接受器。

例如,请考虑提供对医疗数据库进行无限制访问的应用程序。 此类服务的客户机可能需要对该服务进行验证。此方法会信任从该数据库中检索的所有信息。例如,出于保密性方面的考虑,客户机可能不希望暴露其标识。

要请求进行匿名验证,请将 gss_init_sec_context()req_flags 参数设置为 GSS_C_ANON_FLAG。要验证匿名验证是否可用,请检查 gss_init_sec_context()gss_accept_sec_context()ret_flags 参数,查看是否返回了 GSS_C_ANON_FLAG

如果匿名验证有效,则针对 gss_accept_sec_context()gss_inquire_context() 返回的客户机名称调用 gss_display_name() 时会生成通用的匿名名称。


注 –

应用程序负责在所请求的匿名验证不被许可时采取相应的措施。在此类情况下,GSS-API 不会终止上下文。


在 GSS-API 中使用通道绑定

对于许多应用程序来说,建立基本的上下文足以确保对上下文启动器进行正确的验证。如果需要实现额外的安全性,则可以使用 GSS-API 所提供的通道绑定功能。通道绑定是用于标识所使用的特定数据通道的标记。具体来说,通道绑定可标识起点和终点,即上下文的启动器和接受器。由于标记特定于始发者应用程序和接收者应用程序,因此此类标记可为有效标识提供更多证明。

gss_channel_bindings_t 数据类型是一个指针,它指向如下所示的作为通道绑定的 gss_channel_bindings_struct 结构。

typedef struct gss_channel_bindings_struct {

OM_uint32       initiator_addrtype;

gss_buffer_desc initiator_address;

OM_uint32       acceptor_addrtype;

gss_buffer_desc acceptor_address;

gss_buffer_desc application_data;

} *gss_channel_bindings_t;

第一个字段表示地址类型,用于标识所发送的启动器地址采用的格式。第二个字段表示启动器的地址。例如,initiator_addrtype 可能会发送到 GSS_C_AF_INET,表示 initiator_address 采用的是 Internet 地址(即 IP 地址)形式。同样,第三和第四个字段分别表示接受器的地址类型和接受器的地址。应用程序可以根据需要使用最后一个字段 application_data。如果不打算使用 application_data,请将 application_data 设置为 GSS_C_NO_BUFFER。如果没有为某个应用程序指定地址,则应当将该应用程序的地址类型字段设置为 GSS_C_AF_NULLADDR通道绑定的地址类型一节提供了有效地址类型值的列表。

地址类型用于表示地址族,而不是表示特定的寻址格式。 对于包含多种替换地址形式的地址族,initiator_addressacceptor_address 字段中必须包含足够的信息才能确定所使用的形式。 除非另行指定,否则应当以网络字节顺序(即地址族的本机字节排序)指定地址。

要建立使用通道绑定的上下文,gss_init_sec_context()input_chan_bindings 参数应当指向所分配的通道绑定结构。该结构的字段将连接到一个八位字节字符串,并将派生一个 MIC。该 MIC 随后会绑定到输出令牌。然后,应用程序会将该令牌发送到上下文接受器。接受器在收到该令牌之后将调用 gss_accept_sec_context()。有关更多信息,请参见在 GSS-API 中接受上下文gss_accept_sec_context() 会针对所收到的通道绑定计算 MIC。如果 MIC 不匹配,gss_accept_sec_context() 随后将返回 GSS_C_BAD_BINDINGS

由于 gss_accept_sec_context() 会返回已传送的通道绑定,因此接受器可以使用这些值来执行安全检查。例如,接受器可以针对保留在安全数据库中的代码字检查 application_data 的值。


注 –

基础机制可能不会为通道绑定信息提供保密性。因此,除非确保能够实现保密性,否则应用程序不应当在通道绑定中包括敏感信息。要测试保密性,应用程序可以检查 gss_init_sec_context()gss_accept_sec_context()ret_flags 参数。值 GSS_C_CONF_FLAGGSS_C_PROT_READY_FLAG 可用于指示保密性。有关 ret_flags 的信息,请参见在 GSS-API 中启动上下文在 GSS-API 中接受上下文


单个机制可以针对出现在通道绑定中的地址和地址类型施加额外的约束。 例如,一个机制可能会验证通道绑定的 initiator_address 字段是否返回到 gss_init_sec_context()。因此,具有可移植性的应用程序应当为地址字段提供正确的信息。如果无法确定正确的信息,则应当将 GSS_C_AF_NULLADDR 指定为地址类型。

在 GSS-API 中导出和导入上下文

GSS-API 提供了用于导出和导入上下文的方法。此方法允许多进程应用程序(通常是上下文接受器)在进程之间传送上下文。例如,接受器可以有两个进程,一个进程侦听上下文启动器,另一个进程使用在上下文中发送的数据。使用 test_import_export_context() 函数一节说明了如何使用这些函数来保存和恢复上下文。

gss_export_sec_context() 函数可创建进程间令牌,其中包含有关所导出上下文的信息。有关更多信息,请参见GSS-API 中的进程间令牌。调用 gss_export_sec_context() 之前,应当将接收令牌的缓冲区设置为 GSS_C_NO_BUFFER

应用程序随后会将令牌传递到另一个进程。新进程将接受该令牌并将其传递到 gss_import_sec_context()。用于在应用程序之间传递令牌的函数通常也可用于在进程之间传递令牌。

一次只能存在一个安全进程实例。gss_export_sec_context() 可取消激活所导出的上下文并将上下文句柄设置为 GSS_C_NO_CONTEXTgss_export_sec_context() 还可解除配置与该上下文相关联的任何进程范围内的资源。如果无法完成对上下文的导出,gss_export_sec_context() 会保持现有的安全上下文不变,并且不返回进程间令牌。

并非所有机制都允许导出上下文。应用程序可以通过检查 gss_accept_sec_context()gss_init_sec_context()ret_flags 参数来确定上下文是否可以导出。如果此标志设置为 GSS_C_TRANS_FLAG,则可以导出上下文。(请参见在 GSS-API 中接受上下文在 GSS-API 中启动上下文。)

图 4–6 说明多进程接受器如何将上下文导出到多个任务。在这种情况下,进程 1 接收和处理令牌。此步骤会将上下文级别的令牌与数据令牌分开,并将令牌传递到进程 2。进程 2 按照应用程序特定的方式处理数据。在该图中,客户机已经从 gss_init_sec_context() 获取了导出令牌。客户机将令牌传递到用户定义的函数 send_a_token(),该函数指示要传送的令牌是上下文级别的令牌还是消息令牌。send_a_token() 将这些令牌传送到服务器。send_a_token() 可能会用于在线程之间传递令牌,但是该图中未显示这一点。

图 4–6 导出上下文:多线程接受器示例

该图说明了多进程接受器如何将上下文令牌和数据令牌分开并将上下文令牌传递到另一个进程。

在 GSS-API 中获取上下文信息

GSS-API 提供了一个 gss_inquire_context(3GSS) 函数,该函数可用于获取有关指定的安全上下文的信息。请注意,上下文无需是完整的上下文。如果提供了上下文句柄,则 gss_inquire_context() 将提供以下有关上下文的信息:

在 GSS-API 中发送受保护的数据

在对等应用程序之间建立上下文之后,即可在发送消息之前对其进行保护。

建立上下文时使用的只是最基本的 GSS-API 保护:验证。根据基础安全机制,GSS-API 提供了另外两个保护级别:

gss_get_mic()gss_wrap() 之间的区别如下图所示:使用 gss_get_mic(),接受器可获取用于指示消息是否保持不变的标记;使用 gss_wrap(),接受器除了获取标记外还可获取经过加密的消息。

图 4–7 gss_get_mic()gss_wrap()

该图对 gss_get_mic 和 gss_wrap 函数进行比较。

要使用哪个函数取决于具体情况。由于 gss_wrap() 包括完整性服务,因此许多程序都使用 gss_wrap()。程序可以测试保密性服务的可用性,随后可以根据保密性服务的可用情况,以应用或不应用保密性的方式调用 gss_wrap()包装和发送消息即是一个示例。但是,由于无需展开使用 gss_get_mic() 的消息,因此所使用的 CPU 周期比 gss_wrap() 所使用的要少。所以,不需要保密性的程序可能会使用 gss_get_mic() 来保护消息。

使用 gss_get_mic() 标记消息

程序可以使用 gss_get_mic() 向消息添加加密 MIC。接收者可以通过调用 gss_verify_mic() 检查消息的 MIC。

gss_wrap() 相比,gss_get_mic() 会针对消息和 MIC 生成不同的输出,这意味着发送者应用程序必须既能发送消息又能发送随附的 MIC。更重要的是,接收者必须能够区分消息和 MIC。以下方法可确保对消息和 MIC 进行正确的处理:

如果 gss_get_mic() 成功完成,则将返回 GSS_S_COMPLETE。如果指定的 QOP 无效,则将返回 GSS_S_BAD_QOP。有关更多信息,请参见 gss_get_mic(3GSS)

使用 gss_wrap() 包装消息

可以使用 gss_wrap() 函数来包装消息。与 gss_get_mic() 一样,gss_wrap() 也提供了 MIC。如果基础机制允许使用所请求的保密性,则 gss_wrap() 也会对指定的消息进行加密。消息的接收者通过使用 gss_unwrap() 来展开消息。

gss_get_mic() 不同,gss_wrap() 会将消息和 MIC 一起包装到传出消息中。用于传送该包的函数只需调用一次。在另一端,可以使用 gss_unwrap() 提取消息。MIC 对于应用程序不可见。

如果消息已成功包装,则 gss_wrap() 将返回 GSS_S_COMPLETE。如果所请求的 QOP 无效,则将返回 GSS_S_BAD_QOP。有关 gss_wrap() 的示例,请参见包装和发送消息

在 GSS-API 中处理包装大小问题

使用 gss_wrap() 来包装消息会增加要发送的数据量。由于所保护的消息包需要适应指定的传输协议,因此 GSS-API 提供了 gss_wrap_size_limit() 函数。gss_wrap_size_limit() 用于计算可以包装的消息的最大大小,使其不至于因变得过大而不适用于该协议。应用程序可以在调用 gss_wrap() 之前截断超出该大小的消息。在实际包装消息之前,请始终检查包装大小限制。

包装大小的增加取决于以下两个因素:

缺省的 QOP 会因不同的 GSS-API 实现而异。因此,即使指定了缺省的 QOP,所包装消息的大小也会有所不同。下图说明了这种可能性:

该图显示选定的 QOP 如何影响消息大小。

无论是否应用了保密性,gss_wrap() 仍然会增加消息的大小。gss_wrap() 可将 MIC 嵌入到所传送的消息中。但是,对消息进行加密会进一步增加消息的大小。此过程如下图所示。

该图显示使用保密性会增加消息大小。

如果 gss_wrap_size_limit() 成功完成,则将返回 GSS_S_COMPLETE。如果指定的 QOP 无效,则将返回 GSS_S_BAD_QOP包装和发送消息中提供了一个说明如何使用 gss_wrap_size_limit() 返回原始消息最大大小的示例。

成功完成此调用并不一定保证 gss_wrap() 可以保护长度为 max-input-size 个字节的消息。能否保护消息取决于调用 gss_wrap() 时系统资源是否可用。有关更多信息,请参见 gss_wrap_size_limit(3GSS) 手册页。

在 GSS-API 中检测顺序问题

由于上下文启动器会向接受器传送顺序的数据包,因此某些机制允许上下文接受器检查顺序是否正确。这些检查包括数据包是否按正确的顺序到达以及是否存在不必要的数据包重复。请参见下图。接受器可以在检验数据包和展开数据包的过程中检查这两种情况。有关更多信息,请参见展开消息

图 4–8 消息重放和消息失序

该图显示了消息重复和消息失序的情况。

启动器可以使用 gss_init_sec_context() 来检查顺序,方法是通过 GSS_C_REPLAY_FLAGGSS_C_SEQUENCE_FLAGreq_flags 参数应用逻辑 OR

在 GSS-API 中确认消息传送

接收者展开或检验所传送的消息之后,会向发送者返回确认信息,这意味着会发回该消息的 MIC。请考虑以下情况:消息没有由发送者进行包装,而只是通过 gss_get_mic() 使用 MIC 进行了标记。图 4–9 中说明的过程如下所示:

  1. 启动器使用 gss_get_mic() 对消息进行标记。

  2. 启动器将该消息和 MIC 发送到接受器。

  3. 接受器使用 gss_verify_mic() 检验该消息。

  4. 接受器将该 MIC 发回到启动器。

  5. 启动器使用 gss_verify_mic() 来针对原始消息检验所收到的 MIC。

图 4–9 确认 MIC 数据

该图说明如何确认消息完整性代码。

对于已包装的数据,gss_unwrap() 函数从不生成单独的 MIC,因此,接收者必须根据所收到的未包装的消息生成它。图 4–10 中说明的过程如下所示:

  1. 启动器使用 gss_wrap() 包装消息。

  2. 启动器发送已包装的消息。

  3. 接受器使用 gss_unwrap() 展开该消息。

  4. 接受器调用 gss_get_mic() 以生成未包装的消息的 MIC。

  5. 接受器将派生的 MIC 发送到启动器。

  6. 启动器使用 gss_verify_mic() 将所收到的 MIC 与原始消息进行比较。

对于已经为 GSS-API 数据分配的任何数据空间,应用程序应当解除对其进行分配。相关函数包括 gss_release_buffer(3GSS)gss_release_cred(3GSS)gss_release_name(3GSS)gss_release_oid_set(3GSS)

图 4–10 确认已包装的数据

该图说明如何确认已包装并且具有消息完整性代码的消息。

清除 GSS-API 会话

最后,发送和接收所有的消息,之后启动器应用程序和接受器应用程序就将完成操作。此时,这两个应用程序都应当调用 gss_delete_sec_context() 以销毁共享的上下文。gss_delete_sec_context() 可删除与该上下文相关的局部数据结构。

对于已经为 GSS-API 数据分配的任何数据空间,确保应用程序解除对其进行分配是不错的方法。这可以通过 gss_release_buffer()gss_release_cred()gss_release_name()gss_release_oid_set() 函数来完成。