Sun 企业鉴别机制指南

第 8 章 借助 RPCSEC_GSS 进行安全网络编程

进行网络操作的应用程序经常必须确保其事务是安全的。 RPCSEC_GSS 应用程序编程接口 (API) 允许开发者利用各种各样广泛的安全机制,其中包括 SEAM 和 Kerberos V5。同样重要的是, RPCSEC_GSS 包括完整性和隐私服务,提供有超过鉴别的保护。尽管 RPCSEC_GSS 既不是 SEAM 的一部分,也不是其所专有的,但是想要在其应用程序中利用 Kerberos V5 的程序员,均将发现这是一个很好的实现方法。实际上,因为 RPCSEC_GSS 是与机制无关的,并非将 SEAM/ Kerberos V5 用作其安全机制但又想利用隐私和完整性的开发者,均应当考虑使用它。

本章假设您熟悉 RPC 编程;请参见 ONC+ Developer's Guide了解有关 RPC 的信息。另外,本章只是一个总览;如要了解有关 RPCSEC_GSS 的具体方面的信息,诸如函数或数据结构,请参见 rpcsec_gss(3N) 手册页,或针对本章中所描述的任意函数的手册页。

下列主题在本章中得到论述:

安全风格

本节描述 RPCSEC_GSS API 的发展和本质。

RPCSEC_GSS 以前的安全

RPC 所支持的第一批安全风格中的一个是 AUTH_SYS (又作 AUTH_UNIX)。 AUTH_SYS 借助用户和组 ID, 提供了一个 UNIX 风格的资格,用于识别一个消息的发送方和接收方。 AUTH_SYS 易于实施;然而,其也易于绕过,因为其没有提供真正的鉴别 - 也就是说,无法让一个服务器来验证一个客户机实际就是其所声称的身份。因此,在 AUTH_SYS 下伪造一个网络请求就较为容易。

后来的一个安全风格,AUTH_DES,是在 AUTH_SYS 之后不久出现的。 AUTH_DES 基于一个公用密钥鉴别 - 它使用一个 Diffie-Hellman 密钥交换来在一个客户机的私有密钥和一个服务器的公用密钥之间产生一个共有密钥。共有密钥用于为一个 DES 对话密钥加密,而一个服务器为后者解密,以建立一个对话。

尽管 AUTH_DES 代表着一个相对于 AUTH_SYS 的显著的进步,但对广泛应用还是有一些限制。对很多人来讲,主要的反对面就是依照今天的加密标准,其密钥尺寸有些不够长。

最终,另一 RPC 安全风格得到引进。 AUTH_ KERB,基于 Kerberos V4,提供有比 AUTH_DES 或 AUTH_SYS 更好的安全。然而,其还是可以被利用。

如要了解更多有关这些安全风格的信息,请参见 ONC+ Developer's Guide

完整性和隐私:GSS-API

为了改善安全,一个新的网络层,类属安全标准 API,或 GSS-API,得以添加。 GSS-API 框架结构提供了鉴别以外的两个额外的安全 服务


注意:

当前, GSS-API 并未对外批露。然而,通过 RPCSEC_GSS 函数,某些 GSS-API 特性是 "可见"的- 可以用一种 "不透明" 的方式对其进行操纵。程序员无需直接顾及它们的值。


RPCSEC_GSS API

RPCSEC_GSS 安全风格允许 ONC RPC 应用程序来利用 GSS-API 的特性。 RPCSEC_GSS 居于 GSS-API 层的 "顶部",情形如下:

图形 8-1 GSS-API 和 RPCSEC_GSS 安全层

Graphic

使用针对 RPCSEC_GSS, ONC RPC 应用程序的编程接口,可以指定:

机制

一种安全措施。每个种类的安全机制均提供一个不同种类的数据保护,以及一个或多个级别的数据保护。这样的话,就是指 GSS-API 所支持的任何安全机制 (Kerberos V5,RSA 公用密钥,等等)。

安全服务

或是隐私,或是完整性 (或两者都不是)。默认为完整性。服务是与机制无关的。

QOP

保护质量。 QOP 指定用于实施隐私或完整性服务的加密算法的类型。每个安全机制可以有一个或多个 QOP 与其相关联。

应用程序可以通过 RPCSEC_GSS 所提供的函数,获得有效 QOP 和机制的列表。 (请参见 "其它函数"。) 开发者应当避免将机制和 QOP 硬性编码进其应用程序,以便该应用程序不需要修改就可以使用新的或不同的机制和 QOP。


注意:

从历史上讲,"安全风格"和"鉴别风格"指的是同一意思。随着 RPCSEC_GSS 的引进, "风格" 现在拥有一个有些不同的意义。与鉴别一起,风格现在可以包括一个服务 (完整性或隐私),尽管当前 RPCSEC_GSS 是唯一可以做到这一点的风格。


使用 RPCSEC_GSS,ONC RPC 应用程序可以与一个对等机建立一个安全的环境,交换数据,以及销毁环境,就象它们对待其它的风格一样。一旦一个环境得到建立,应用程序就可以为所发送的每个数据单元更改 QOP 和服务。

如要了解更多有关 RPCSEC_GSS 的信息,其中包括 RPCSEC_GSS 数据类型,请参见 rpcsec_gss(3N) 手册页。

RPCSEC_GSS 例程

表 8-1 总结 RPCSEC_GSS 命令。这只是 RPCSEC_GSS 函数的一个一般总览,而不是对其中每个函数的具体描述。如要了解更多有关每个函数的信息,请参见其手册页,或检查 rpcsec_gss(3N) 手册页,获得一个总览,其中包括 RPCSEC_GSS 数据结构的一个列表。

表 8-1 RPCSEC_GSS 函数
动作函数输入输出
 创建一个安全环境rpc_gss_seccreate() 客户机句柄,授权对象名称,机制, QOP, 服务类型AUTH 句柄
 为环境更改 QOP, 服务类型rpc_gss_set_defaults() 旧的 QOP, 服务 新的QOP, 服务
 显示安全形变之前数据的最大尺寸rpc_gss_max_data_length() 传送所允许的最大数据尺寸 最大的形变前数据尺寸
 显示安全形变之前数据的最大尺寸rpc_gss_svc_max_data_length() 传送所允许的最大数据尺寸 最大形变前数据尺寸
 设定服务器所要代表的授权对象的名称rpc_gss_set_svc_name() 授权对象名称, RPC 程序,版本号 如果成功则为 TRUE
 取出调用方 (客户机) 的资格rpc_gss_getcred()svc_req 结构的指针 UNIX 资格, RPCSEC_GSS 资格, cookie
 指定 (用户所写的) 回调函数rpc_gss_set_callback() 到回调函数的指针 如果成功则为 TRUE
 从独特的参数为授权对象名称创建 RPCSEC_GSS 结构rpc_gss_get_principal_name() 机制,用户名,机器名称,域名 RPCSEC_GSS 授权对象名称结构
 在一个 RPCSEC_GSS 例程失败时取出一个错误代码rpc_gss_get_error()   RPCSEC_GSS 错误号码,errno如果适用的话
 获得已安装机制的字符串rpc_gss_get_mechanisms()    有效机制的列表
 获得有效的 QOP 字符串rpc_gss_get_mech_info() 机制 该机制的有效 QOP
 获得所支持的 RPCSEC_GSS 的最高,最低版本号rpc_gss_get_versions()    最高,最低版本
 检查以确定某个机制是否已安装rpc_gss_is_installed() 机制 如果已安装则为 TRUE
 将 ASCII 机制转换为 RPC 对象标识符rpc_gss_mech_to_oid() 机制 (作为字符串) 机制 (作为 OID)
 将 ASCII QOP 转换为整数rpc_gss_qop_to_num() QOP (作为字符串) QOP (作为整数)

创建一个环境

环境是借助 rpc_gss_seccreate() 调用加以创建的。该函数将下列作为其变元:

它返回一个 AUTH 鉴别句柄。实例 8-1 显示如何可以借助 Kerberos V5 安全机制和完整性服务,用 rpc_gss_seccreate() 来创建一个环境:


实例 8-1 rpc_gss_seccreate ()

CLIENT *clnt;                    /* client handle */
char server_host[] = "foo"; 
char service_name[] = "nfs@eng.acme.com"; 
char mech[] = "kerberos_v5"; 

clnt = clnt_create(server_host, SERVER_PROG, SERV_VERS, "netpath"); 
clnt->clnt_auth = rpc_gss_seccreate(clnt, service_name, mech,    
                          rpc_gss_svc_integrity, NULL, NULL, NULL);  

. . .

关于 实例 8-1,需要注意的一些事宜是:

如要了解更多的信息,请参见 rpc_gss_seccreate(3N) 手册页。

更改值和销毁一个环境

一旦一个环境业已设定,应用程序可能需要为正在传输的单个的数据单元更改 QOP 和服务值。(例如,您可能会需要一个程序为一个口令加密,但并非一个登录名称。) rpc_gss_set_defaults() 允许您办到这一点:


实例 8-2 rpc_gss_set_defaults ()

rpc_gss_set_defaults(clnt->clnt_auth, rpc_gss_svc_privacy, qop);

 . . .

这样的话,安全服务设定为隐私 (请参见 "创建一个环境")。 qop 是到为一个新的 QOP 命名的字符串的一个指针。

环境以通常的方法得到销毁,借助 auth_destroy()

如要了解更多有关更改服务和 QOP 的信息,请参见 rpc_gss_set_defaults(3N) 手册页。

授权对象名称

如要建立和维护一个安全环境,需要两个类型的授权对象名称:

设定服务器授权对象名称

服务器需要在启动时被告知其将代表的授权对象的名称。 (一个服务器可以作为一个以上的授权对象行事。) rpc_gss_set_svc_name() 设定授权对象的名称:


实例 8-3 rpc_gss_set_svc_name()

char *principal, *mechanism; 
u_int req_time; 

principal = "nfs@eng.acme.com"; 
mechanism = "kerberos_v5"; 
req_time = 10000;		/* time for which credential should be valid */ 

rpc_gss_set_svc_name(principal, mechanism, req_time, SERV_PROG, SERV_VERS);

(Kerberos 忽略 req_time 参数。其它的鉴别系统可能会使用它。)

如要了解更多的信息,请参见 rpc_gss_set_svc_name(3N) 手册页。

生成客户机授权对象名称

服务器需要能够对一个客户机的授权对象名称进行操作 - 例如,为了将一个客户机的授权对象名称与一个访问控制列表进行比较,或为该客户机检索一个 UNIX 资格,如果此类的一个资格存在的话。该授权对象名称以 rpc_gss_principal_t 结构指针的形式得到保存。(请参见 rpcsec_gss(3N) 手册页,了解更多有关 rpc_gss_principal_t 的信息。) 如果一个服务器想要将其业已收到的一个授权对象名称与一个已知实体的名称进行比较,则其需要能够以该形式生成一个授权对象名称。

rpc_gss_get_principal_name() 调用将对网络上某个个体进行独特识别的多个参数作为输入,生成一个授权对象名称,作为一个 rpc_gss_principal_t 结构指针:


实例 8-4 rpc_gss_get_principal_name ()

rpc_gss_principal_t *principal; 

rpc_gss_get_principal_name(principal, mechanism, name, node, domain); 

. . .

用于 rpc_gss_get_principal_name() 的变元如下:

每个安全机制要求不同的识别参数。例如, Kerberos V5 要求一个用户名以及可选的合格节点和域的名称 (以 Kerberos 术语讲,就是主机和区域名)。

如要了解更多的信息,请参见 rpc_gss_get_principal_name(3N) 手册页。

释放授权对象名称

授权对象名称是使用 释放() 库调用来加以释放的。

在服务器接收资格

服务器必须能够取出一个客户机的资格。 rpc_gss_getcred() 函数,如 实例 8-5 所示,允许服务器来取回 UNIX 资格或 RPCSEC_GSS 资格 (或两者,就其而言)。这是通过两个变元而做到的,如果函数成功的话,这两个变元就得到设定。一个变元是到一个 rpc_gss_ucred_t 结构的指针,它包含调用方的 UNIX 资格,如果其资格存在的话:

typedef struct {
    uid_t   uid;          /* user ID */
    gid_t   gid;          /* group ID */
    short   gidlen;       
    git_t   *gidlist;     /* list of groups */
} rpc_gss_ucred_t;

另一个变元是到一个 rpc_gss_raw_cred_t 结构的指针,其看似:

typedef struct {
		u_int                  version;               /* RPCSEC_GSS program version */
		char                   *mechanism;
		char                   *qop;
		rpc_gss_principal_t    client_principal;     /* client principal name */
		char                   *svc_principal;        /* server principal name */
		rpc_gss_service_t			service;               /* privacy, integrity enum */
} rpc_gss_rawcred_t;
(请参见 "生成客户机授权对象名称",了解对 rpc_gss_principal_t 结构以及其创建方式的描述。) 因为 rpc_gss_rawcred_t 包含客户机和服务器授权对象名称两者,rpc_gss_getcred() 可以返回它们两者。

实例 8-5 是一个简单的服务器侧的调度过程的一个示例,其中服务器为调用方获得资格。该过程获得调用方的 UNIX 资格,然后使用在 rpc_gss_rcred_t 变元找到的机制、 QOP 和服务类型,验证用户的身份。


实例 8-5 获得资格

static void server_prog(struct svc_req *rqstp, SVCXPRT *xprt) 
{ 		rpc_gss_ucred_t *ucred;
  		rpc_gss_rawcred_t *rcred;

  		if (rqst->rq_proq == NULLPROC) {
  			svc_sendreply(xprt, xdr_void, NULL);
 			return;
 		}
 		/*
 		 * authenticate all other requests */
 		 */

 		switch (rqstp->rq_cred.oa_flavor) {
 		case RPCSEC_GSS:
 			/*
 			 * get credential information
 			 */
 			rpc_gss_getcred(rqstp, &rcred, &ucred, NULL);
 			/*
 			* verify that the user is allowed to access
 			* using received security parameters by
 			* peeking into my config file
 			*/
 			if (!authenticate_user(ucred->uid, rcred->mechanism,
 				rcred->qop, rcred->service)) {
 				svcerr_weakauth(xprt);
 				return;
 			}
 			break; 	/* allow the user in */
 		default:
 			svcerr_weakauth(xprt);
 			return;
 		} /* end switch */

 		switch (rqstp->rq_proq) {
 		case SERV_PROC1:
 			. . .
 		}

 		/* usual request processing; send response ... */

 		return;

 }

如要了解更多的信息,请参见 rpc_gss_getcred(3N) 手册页。

Cookie

Missing Cross Reference Target 中,用于 rpc_gss_getcred() 的最后一个变元 (这里是一个 NULL) 是一个用户定义的 cookie, 返回时其值是环境得到创建时服务器所指定的任何内容。该 cookie 是一个四字节的值,可以以适合于应用程序的任何方法加以利用 - RPC 并不对其加以解释。例如, cookie 可以是到一个结构的指针或索引,该结构代表环境初始器;服务器在环境创建时间进行计算,而不是为每个请求都计算该值 (从而节省请求处理时间)。

回调

可以使用 cookie 的另一个地方就是回调。服务器可以指定一个 (用户定义的) 回调,以便知道某个环境首次得到使用的时间,方法是通过使用 rpc_gss_set_callback() 函数。在为所指定的程序和版本建立环境之后,某个环境第一次用于数据交换时,就调用回调。

用户定义的回调例程具有下面的形式:

第二个和第三个变元, deleggss_context是 GSS-API 数据类型,且当前尚未对外披露,因而回调函数可以将其忽略。 (简而言之, deleg 是任何所代表的对等机的身份,而 gss_context 是一个到 GSS-API 环境的指针,以备程序想在环境上进行 GSS-API 操作 - 即,测试接受范畴。) 我们业已见过 cookie 变元。

lock 变元是到一个 rpc_gss_lock_t 结构的指针:

typedef struct {
 		bool_t              locked;
 		rpc_gss_rawcred_t   *raw_cred;
 } rpc_gss_lock_t;
该参数使得一个服务器为对话强制执行一个具体的 QOP 和服务。QOP 和服务见于 实例 8-5 中所描述的 rpc_gss_rawcred_t 结构。 (服务器不应更改服务和 QOP 的值。) 当用户定义的回调被调用时, locked 字段就设定为 FALSE。如果服务器将 locked 设定为 TRUE,则只有其 QOP 和rpc_gss_rawcred_t 结构中的服务值与 QOP 和服务值相匹配的请求被接受。

如要了解更多的信息,请参见 rpc_gss_set_callback(3N) 手册页。

最大数据尺寸

两个函数 - rpc_gss_max_data_length()rpc_gss_svc_max_data_length() - 对于在一块数据通过安全措施得到形变和"跨越线路"得到发送之前就确定其长度十分有用。即,一个安全形变,诸如加密,通常变更所传输的数据块的尺寸 (最经常是将其增大)。如要确保数据不会增大超过一个适用的尺寸,这两个函数 - 前一个是客户机侧的版本,后一个是服务器侧的版本- 为某一给定传送返回最大形变前尺寸。

如要了解更多的信息,请参见 rpc_gss_max_data_length(3N)rpc_gss_svc_max_data_length(3N) 手册页.

其它函数

要获得关于已安装的安全系统的信息,有多个函数十分有用:

程序员使用这些函数,就可以避免在应用程序中使用硬性编码安全参数。 (请参见 表 8-1rpcsec_gss(3N) 手册页,获得所有 RPCSEC_GSS 函数的一个列表。)

相关的文件

RPCSEC_GSS 利用某些文件来存储信息。

gsscred

当一个服务器取回与一个请求相关联的客户机资格时,它就可以获得客户机的授权对象名称 (以一个 rpc_gss_principal_t 结构指针的形式) 或该客户机的本地 UNIX 资格 (UID)。诸如 NFS 的服务为了进行访问检查,要求有一个本地 UNIX 资格,但是其它服务可能无此要求;例如,这些服务可以直接在其自己的访问控制列表中,将授权对象名称存为一个 rpc_gss_principal_t 结构。


注意:

一个客户机的网络资格 (其授权对象名称) 和任何本地 UNIX 资格之间的通讯并不是自动的 - 其必须由本地安全管理员明确地加以设置。


gsscred 文件包含客户机的 UNIX 和网络 (例如, Kerberos V5) 资格两者。(后者是 rpc_gss_principal_t 结构的十六进制 ASCII 表示。) 其是通过 XFN 加以访问的;从而,该表可以跨越文件、NIS、NIS+或 XFN 所支持的任意为了的名称服务加以实施。在 XFN 分级结构中,该表是作为 this_org_unit/服务/gsscred 出现的。 gsscred 表是借助gsscred 实用程序进行维护的,允许管理员添加和删除用户和机制。

/etc/gss/qop/etc/gss/mech

出于方便, RPCSEC_GSS 使用字符串文字来表示机制和保护质量 (QOP) 参数。然而,底层机制自身却要求机制表示为对象标识符,各种 QOP 表示为 32 位的整数。另外,对于每个机制,还必须指定为该机制实施服务的共享库。

/etc/gss/mech 文件存储下列有关系统上所有已安装机制的信息: 机制名称, 以 ASCII 形式;机制的 OID; 实施该机制所提供的服务的共享库;以及可选的实施服务的核心模块。样本行可能会看似为:


 Kerberos_v5   1.2.840.113554.1.2.2    gl/mech_krb5.so gl_kmech_krb5

对于所有已安装的机制, /etc/gss/qop 文件存储每个机制所支持的所有 QOP,且使用 ASCII 字符串及其相应的 32 位整数这两种形式。

当安全机制首次安装于某个给定的系统上时,/etc/gss/mech/etc/gss/qop 两者均得到创建。

因为许多核心内 RPC 例程使用非字符串值来代表机制和 QOP, 应用程序可以使用 rpc_gss_mech_to_oid()rpc_gss_qop_to_num() 函数来获得这些参数的非字符串等效物,以备其需要利用这些核心内例程。