进行网络操作的应用程序经常必须确保其事务是安全的。 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 的发展和本质。
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。
为了改善安全,一个新的网络层,类属安全标准 API,或 GSS-API,得以添加。 GSS-API 框架结构提供了鉴别以外的两个额外的安全 服务:
完整性。与完整性服务一起, GSS-API 使用底层机制来鉴别程序之间所交换的消息。加密校验和建立:
数据发端方对接收方的身份
接收方对发端方的身份 (如果相互鉴别被请求)
所传输的数据自身的可靠性
隐私。隐私服务包括完整性服务。另外,所传输的数据也 经过加密,以便使其受到保护,防备任何的偷听者。
由于美国输出限制,隐私服务可能不是所有的 SEAM 用户均可用的。
当前, GSS-API 并未对外批露。然而,通过 RPCSEC_GSS 函数,某些 GSS-API 特性是 "可见"的- 可以用一种 "不透明" 的方式对其进行操纵。程序员无需直接顾及它们的值。
RPCSEC_GSS 安全风格允许 ONC RPC 应用程序来利用 GSS-API 的特性。 RPCSEC_GSS 居于 GSS-API 层的 "顶部",情形如下:
使用针对 RPCSEC_GSS, ONC RPC 应用程序的编程接口,可以指定:
一种安全措施。每个种类的安全机制均提供一个不同种类的数据保护,以及一个或多个级别的数据保护。这样的话,就是指 GSS-API 所支持的任何安全机制 (Kerberos V5,RSA 公用密钥,等等)。
或是隐私,或是完整性 (或两者都不是)。默认为完整性。服务是与机制无关的。
保护质量。 QOP 指定用于实施隐私或完整性服务的加密算法的类型。每个安全机制可以有一个或多个 QOP 与其相关联。
应用程序可以通过 RPCSEC_GSS 所提供的函数,获得有效 QOP 和机制的列表。 (请参见 "其它函数"。) 开发者应当避免将机制和 QOP 硬性编码进其应用程序,以便该应用程序不需要修改就可以使用新的或不同的机制和 QOP。
从历史上讲,"安全风格"和"鉴别风格"指的是同一意思。随着 RPCSEC_GSS 的引进, "风格" 现在拥有一个有些不同的意义。与鉴别一起,风格现在可以包括一个服务 (完整性或隐私),尽管当前 RPCSEC_GSS 是唯一可以做到这一点的风格。
使用 RPCSEC_GSS,ONC RPC 应用程序可以与一个对等机建立一个安全的环境,交换数据,以及销毁环境,就象它们对待其它的风格一样。一旦一个环境得到建立,应用程序就可以为所发送的每个数据单元更改 QOP 和服务。
如要了解更多有关 RPCSEC_GSS 的信息,其中包括 RPCSEC_GSS 数据类型,请参见 rpcsec_gss(3N) 手册页。
表 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() 调用加以创建的。该函数将下列作为其变元:
一个客户机句柄 (例如,由 clnt_create() 所返回的)
服务器授权对象的名称 (例如,nfs@acme.com)
用于对话的机制 (例如, Kerberos V5)
安全服务类型 (例如,隐私)
用于对话的 QOP
可以为大多数的使用情况保持为不透明的两个 GSS-API 参数 (也就是说,程序员可以提供 NULL 值)
它返回一个 AUTH 鉴别句柄。实例 8-1 显示如何可以借助 Kerberos V5 安全机制和完整性服务,用 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_get_mechanisms() 从一个可用机制表程序性地将其获得,则更为常见。
QOP 是作为一个 NULL 传递的,将 QOP 设定为该机制的默认值。否则,和机制一样,可以借助 rpc_gss_get_mechanisms() 程序性地获得一个有效的值。请参见 rpc_gss_get_mechanisms(3N) 手册页,了解更多的信息。
安全服务类型,rpc_gss_svc_integrity,是一个 enum,其 RPCSEC_GSS 类型为 rpc_gss_service_t。 rpc_gss_service_t 有下列格式:
typedef enum { rpc_gss_svc_default = 0, rpc_gss_svc_none = 1, rpc_gss_svc_integrity = 2, rpc_gss_svc_privacy = 3 } rpc_gss_service_t;
默认的安全服务映射到完整性,因而程序员本来可以指定 rpc_gss_svc_default 而获得同一结果。
如要了解更多的信息,请参见 rpc_gss_seccreate(3N) 手册页。
一旦一个环境业已设定,应用程序可能需要为正在传输的单个的数据单元更改 QOP 和服务值。(例如,您可能会需要一个程序为一个口令加密,但并非一个登录名称。) 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) 手册页。
服务器 授权对象名称。服务器的授权对象名称总是指定为一个 NULL 结尾的 ASCII 字符串,其格式为 服务@主机 - 例如, nfs@eng.acme.com。
当一个客户机创建一个安全环境时,它就以该格式指定服务器授权对象名称 (请参见 "创建一个环境")。与此类似,当一个服务器需要设定其将代表的一个授权对象的名称时,它就使用 rpc_gss_set_svc_name(),这接收该格式的一个授权对象名称,作为一个变元。
客户机 授权对象名称。一个客户机的授权对象名称,即服务器所收到的,其格式为 rpc_gss_principal_t 结构:一个经过计数的,不透明的字节字符串,由正在使用的机制加以确定。该结构在 rpcsec_gss(3N) 手册页上有描述。
服务器需要在启动时被告知其将代表的授权对象的名称。 (一个服务器可以作为一个以上的授权对象行事。) 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 结构指针:
rpc_gss_principal_t *principal; rpc_gss_get_principal_name(principal, mechanism, name, node, domain); . . .
用于 rpc_gss_get_principal_name() 的变元如下:
授权对象 是一个到需要设定的 rpc_gss_principal_t 结构的指针。
机制 是所使用的安全机制 (请记住,所生成的授权对象名称是与机制有关的)。
名称 是一个个体或服务名称,诸如 joeh 或 nfs,乃至一个用户定义的应用程序的名称。
例如, node 可能会是一个 UNIX 机器名称。
例如, domain 可能会是一个 DNS, NIS, 或 NIS+ 域名,或者是一个 Kerberos 区域。
每个安全机制要求不同的识别参数。例如, 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 和服务类型,验证用户的身份。
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) 手册页。
在 Missing Cross Reference Target 中,用于 rpc_gss_getcred() 的最后一个变元 (这里是一个 NULL) 是一个用户定义的 cookie, 返回时其值是环境得到创建时服务器所指定的任何内容。该 cookie 是一个四字节的值,可以以适合于应用程序的任何方法加以利用 - RPC 并不对其加以解释。例如, cookie 可以是到一个结构的指针或索引,该结构代表环境初始器;服务器在环境创建时间进行计算,而不是为每个请求都计算该值 (从而节省请求处理时间)。
可以使用 cookie 的另一个地方就是回调。服务器可以指定一个 (用户定义的) 回调,以便知道某个环境首次得到使用的时间,方法是通过使用 rpc_gss_set_callback() 函数。在为所指定的程序和版本建立环境之后,某个环境第一次用于数据交换时,就调用回调。
用户定义的回调例程具有下面的形式:
第二个和第三个变元, deleg 和 gss_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) 手册页.
rpc_gss_get_mechanisms() 返回已安装安全机制的一个列表
rpc_gss_is_installed() 检查某个指定的机制是否已安装
rpc_gss_get_mech_info() 返回一个给定机制的各种有效的 QOP
程序员使用这些函数,就可以避免在应用程序中使用硬性编码安全参数。 (请参见 表 8-1 和 rpcsec_gss(3N) 手册页,获得所有 RPCSEC_GSS 函数的一个列表。)
RPCSEC_GSS 利用某些文件来存储信息。
当一个服务器取回与一个请求相关联的客户机资格时,它就可以获得客户机的授权对象名称 (以一个 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 实用程序进行维护的,允许管理员添加和删除用户和机制。
出于方便, 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() 函数来获得这些参数的非字符串等效物,以备其需要利用这些核心内例程。