Solaris 开发者安全性指南

在 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() 将提供以下有关上下文的信息: