面向开发者的 Oracle® Solaris 11 安全性指南

退出打印视图

更新时间: 2014 年 7 月
 
 

GSS-API 数据类型

以下几节介绍了主要的 GSS-API 数据类型。有关所有 GSS-API 数据类型的信息,请参见GSS-API Data Types and Values

GSS-API 整数

由于 int 的长度因平台而异,因此 GSS-API 提供了以下整数数据类型:OM_uint32,此为 32 位无符号整数。

GSS-API 中的字符串和类似数据

由于 GSS-API 以内部格式处理所有数据,因此在将字符串传递到 GSS-API 函数之前必须将其转换为 GSS-API 格式。GSS-API 可处理具有 gss_buffer_desc 结构的字符串:

typedef struct gss_buffer_desc_struct {
     size_t     length;
     void       *value;
}  gss_buffer_desc *gss_buffer_t;

gss_buffer_t 是指向此类结构的指针。在将字符串传递到使用它们的函数之前,必须先将其放到 gss_buffer_desc 结构中。在以下示例中,通用的 GSS-API 函数在发送消息之前会先对此消息应用保护机制。

示例 4-1  在 GSS-API 中使用字符串
char *message_string;
gss_buffer_desc input_msg_buffer;

input_msg_buffer.value = message_string;
input_msg_buffer.length = strlen(input_msg_buffer.value) + 1;

gss_generic_function(arg1, &input_msg_buffer, arg2...);

gss_release_buffer(input_msg_buffer);

请注意,完成对 input_msg_buffer 的操作之后,必须通过 gss_release_buffer() 取消对 input_msg_buffer 的分配。

gss_buffer_desc 对象不只是用于字符串。例如,令牌可以作为 gss_buffer_desc 对象进行处理。有关更多信息,请参见GSS-API Tokens

GSS-API 中的名称

名称是指主体。在网络安全术语中,主体是指用户、程序或计算机。主体可以是客户机或服务器。

    下面是主体的一些示例:

  • 登录到另一台计算机的用户,如 user@machine

  • 网络服务,如 nfs@machine

  • 运行应用程序的计算机,如 myHost@example.com

在 GSS-API 中,名称会存储为 gss_name_t 对象,该对象对于应用程序是不透明的。名称可以通过 gss_import_name() 函数从 gss_buffer_t 对象转换为 gss_name_t 形式。所导入的每个名称都有一个指示名称格式的相关名称类型。有关名称类型的更多信息,请参见GSS-API OIDs。有关有效名称类型的列表,请参见Name Types

gss_import_name() 具有以下语法:

OM_uint32 gss_import_name (
       OM_uint32          *minor-status,
       const gss_buffer_t input-name-buffer,
       const gss_OID      input-name-type,
       gss_name_t         *output-name)
minor-status

基础机制返回的状态码。请参见GSS-API Status Codes

input-name-buffer

包含要导入的名称的 gss_buffer_desc 结构。应用程序必须显式分配此结构。请参见Strings and Similar Data in GSS-API以及Example 4–2。应用程序不再使用为该参数分配的空间时,必须通过 gss_release_buffer() 取消分配该参数。

input-name-type

用于指定 input-name-buffer 的格式的 gss_OID。请参见Name Types in GSS-API。另外,Name Types中还包含一个有效名称类型表。

output-name

接收名称的 gss_name_t 结构。

Example 4–1 中所示的通用示例进行了小修改,说明如何使用 gss_import_name()。首先,将规则字符串插入到 gss_buffer_desc 结构中。然后,使用 gss_import_name() 将该字符串放到 gss_name_t 结构中。

示例 4-2  使用 gss_import_name()
char *name_string;
gss_buffer_desc input_name_buffer;
gss_name_t      output_name_buffer;

input_name_buffer.value = name_string;
input_name_buffer.length = strlen(input_name_buffer.value) + 1;

gss_import_name(&minor_status, input_name_buffer, 
                    GSS_C_NT_HOSTBASED_SERVICE, &output_name);

gss_release_buffer(input_name_buffer);

使用 gss_display_name() 可以将所导入的名称放回到 gss_buffer_t 对象中,以便能够以可读格式显示。但是,鉴于基础机制存储名称的方式,gss_display_name() 不能保证所得到的字符串与原来的字符串相同。GSS-API 包括若干个用于处理名称的其他函数。请参见GSS-API Functions

gss_name_t 结构可以包含一个名称的多个版本。可以为 GSS-API 所支持的每个机制都生成一个版本。也即是说,user@company 的 gss_name_t 结构可能会包含该名称的两个版本:一个版本由 Kerberos v5 提供,另一个版本由其他机制提供。gss_canonicalize_name() 函数将内部名称和机制用作输入。gss_canonicalize_name() 可生成另一个内部名称,其中包含一个特定于该机制的名称版本。

此类特定于机制的名称称为机制名称 (Mechanism Name, MN)。机制名称是指指定机制所生成的主体名称,而不是指机制的名称。下图对此流程进行了说明。

图 4-3  内部名称和机制名称

image:该图说明如何派生机制名称。

在 GSS-API 中比较名称

请考虑以下情况:服务器从客户机收到一个名称,并且需要在访问控制列表中查找该名称。访问控制列表(即 ACL)是指具有特定访问权限的主体的列表。

    下面是一种用于进行查找的方法:

  1. 使用 gss_import_name() 以 GSS-API 内部格式导入客户机名称(如果该名称尚未导入)。

    在某些情况下,服务器将以内部格式接收名称,因此可不必执行该步骤。例如,服务器可能会查找客户机本身的名称。在启动上下文的过程中,客户机本身的名称以内部格式进行传递。

  2. 使用 gss_import_name() 将每个名称导入到 ACL 中。

  3. 使用 gss_compare_name() 将所导入的每个 ACL 名称与所导入的客户机名称进行比较。

此过程如下图所示。在本示例中,假设步骤 1 是必需的。

图 4-4  比较名称(慢速)

image:该图说明如何使用 gss_compare_name 函数比较客户机的内部名称。

如果只有少数名称,则可以接受使用上述单独比较名称的方法。如果有大量名称,则使用 gss_canonicalize_name() 函数会更高效。

    此方法会执行以下步骤:

  1. 使用 gss_import_name() 导入客户机的名称(如果该名称尚未导入)。

    和上述比较名称的方法一样,如果名称已经采用内部格式,则不必执行此步骤。

  2. 使用 gss_canonicalize_name() 生成机制名称版本的客户机名称。

  3. 使用 gss_export_name() 生成所导出的名称,这是连续字符串形式的客户机名称。

  4. 使用 memcmp() 对所导出的客户机名称与 ACL 中的每个名称进行快速比较,该函数的开销较低。

此过程如下图所示。同样,再次假设服务器需要导入从客户机收到的名称。

图 4-5  比较名称(快速)

image:该图说明如何使用 memcmp 函数比较客户机的内部名称。

由于 gss_export_name() 需要使用机制名称 (Mechanism Name, MN),因此必须首先针对客户机名称运行 gss_canonicalize_name()

有关更多信息,请参见 gss_export_name(3GSS)gss_import_name(3GSS)gss_canonicalize_name(3GSS)

GSS-API OID

    对象标识符 (Object Identifier, OID) 用于存储以下几种数据:

  • 安全机制

  • QOP-保护质量的值

  • 名称类型

OID 存储在 GSS-API gss_OID_desc 结构中。GSS-API 提供了指向 gss_OID 结构的指针,如以下示例中所示。

示例 4-3  OID 结构
typedef struct gss_OID_desc_struct {
        OM_uint32   length;
        void        *elements;
     } gss_OID_desc, *gss_OID;

并且,gss_OID_set_desc 结构中可能会包含一个或多个 OID。

示例 4-4  OID 集合结构
typedef struct gss_OID_set_desc_struct {
        size_t    count;
        gss_OID   elements;
     } gss_OID_set_desc, *gss_OID_set;

Caution

注意  - 应用程序不应尝试使用 free() 取消分配 OID。


GSS-API 中的机制和 QOP

尽管 GSS-API 允许应用程序选择基础安全机制,但是应用程序还是应当尽可能使用 GSS-API 已选择的缺省机制。同样,尽管 GSS-API 允许应用程序指定用于保护数据的保护质量级别,但还是应当尽可能使用缺省 QOP。可以通过向需要使用机制或 QOP 作为参数的函数传递值 GSS_C_NULL_OID 来表示接受缺省机制。


Caution

注意  - 显式指定安全机制或 QOP 会背离使用 GSS-API 的初衷。类似的特定选择会限制应用程序的可移植性。其他 GSS-API 实现可能无法以预期方式支持 QOP 或机制。尽管如此,Appendix D, Specifying an OID 简要介绍了如何查找可用的机制和 QOP 以及如何从中选择一个机制或 QOP。


GSS-API 中的名称类型

除了 QOP 和安全机制以外,OID 还用于指示名称类型,从而指明关联名称的格式。例如,gss_import_name() 函数(用于将主体的名称从字符串转换为 gss_name_t 类型)将需要转换的字符串的格式用作一个参数。例如,如果名称类型为 GSS_C_NT_HOSTBASED_SERVICE,则该函数便会知道所输入的名称是采用 service@host 形式。如果名称类型为 GSS_C_NT_EXPORT_NAME,则该函数需要使用 GSS-API 导出的名称。应用程序可以使用 gss_inquire_names_for_mech() 函数来确定指定机制可用的名称类型。Name Types中提供了 GSS-API 所使用的名称类型的列表。