本节讨论使用权限的接口。要使用权限编程接口,需要以下头文件。
#include <priv.h>
本节还提供了说明如何在特权应用程序中使用权限接口的示例。
以下是权限接口使用的主要数据类型:
权限类型-单个权限由 priv_t 类型定义表示。可以按照以下方式使用权限 ID 字符串初始化 priv_t 类型的变量:
priv_t priv_id = PRIV_FILE_DAC_WRITE;
权限集类型-权限集由 priv_set_t 数据结构表示。请使用表 2–1 中列出的某个权限处理函数来初始化 priv_set_t 类型的变量。
权限操作类型-对文件或进程权限集执行的操作类型由 priv_op_t 类型定义表示。并不是所有的操作对每种类型的权限集都有效。有关详细信息,请阅读使用权限进行编程中的权限集说明。
下表列出了使用权限的接口。表后面提供了一些主要权限接口的说明。
表 2–1 使用权限的接口
目的 |
函数 |
其他注释 |
---|---|---|
获取和设置权限集 |
setppriv() 和 getppriv() 是系统调用。priv_ineffect() 和 priv_set() 是为方便而使用的包装函数。 |
|
识别和转换权限 |
priv_str_to_set(3C)、priv_set_to_str(3C)、priv_getbyname(3C)、priv_getbynum(3C)、priv_getsetbyname(3C)、priv_getsetbynum(3C) |
这些函数将指定的权限或权限集映射到名称或编号。 |
处理权限集 |
priv_allocset(3C)、priv_freeset(3C)、priv_emptyset(3C)、priv_fillset(3C)、priv_isemptyset(3C)、priv_isfullset(3C)、priv_isequalset(3C)、priv_issubset(3C)、priv_intersect(3C)、priv_union(3C)、priv_inverse(3C)、priv_addset(3C)、priv_copyset(3C)、priv_delset(3C)、priv_ismember(3C) |
这些函数与权限内存分配、测试和各种设置操作有关。 |
获取和设置进程标志 |
PRIV_AWARE 进程标志指示进程是否了解权限或是否在超级用户模型下运行。PRIV_DEBUG 用于权限调试。 |
|
低级凭证处理 |
这些例程用于调试、底层系统调用和内核调用。 |
用于设置权限的主要函数为 setppriv(),该函数具有以下语法:
int setppriv(priv_op_t op, priv_ptype_t which, \ const priv_set_t *set);
op 表示要执行的权限操作。op 参数具有以下三个可能值之一:
PRIV_ON-将 set 变量指定的权限添加到 which 指定的权限集类型中
PRIV_OFF-从 which 指定的权限集类型中删除 set 变量指定的权限
PRIV_SET-使用 set 变量指定的权限替换 which 指定的权限集类型中的权限
which 用于指定要更改的权限集类型:
PRIV_PERMITTED
PRIV_EFFECTIVE
PRIV_INHERITABLE
PRIV_LIMIT
set 指定要在更改操作中使用的权限。
此外,还提供了便利函数:priv_set()。
这些函数便于使用其数值映射权限名称。priv_str_to_set() 是此系列中的典型函数。priv_str_to_set() 具有以下语法:
priv_set_t *priv_str_to_set(const char *buf, const char *set, \ const char **endptr);
priv_str_to_set() 采用 buf 中指定的权限名字符串。priv_str_to_set() 返回可以与四个权限集之一组合的一组权限值。**endptr 可用于调试解析错误。请注意,可以在 buf 中包括以下关键字:
“all” 指示所有已定义的权限。使用 “all,!priv_name,...” 可以指定除指示权限以外的所有权限。
使用 “priv_set, !priv_name,...” 的构造将从指定的权限集中删除指定的权限。如果事先没有指定集,请不要使用 “!priv_name,...”,因为如果没有从中删除权限的权限集,该构造将从空的权限集中删除指定的权限,并有效指示无权限。
“none” 指示无权限。
“basic” 表示执行登录标准 UNIX 操作系统的所有用户一般都可以执行的操作所需的权限集。
本节对使用超级用户模型和最低权限模型包括权限的方式进行比较。
/* Program start */ uid = getuid(); seteuid(uid); /* Privilege bracketing */ seteuid(0); /* Code requiring superuser capability */ ... /* End of code requiring superuser capability */ seteuid(uid); ... /* Give up superuser ability permanently */ setreuid(uid,uid);
此示例说明如何在最低权限模型中包括特权操作。此示例使用以下假定:
该程序为 setuid 0。
由于 setuid 0,允许集和有效集最初设置为所有权限。
可继承集最初设置为基本权限。
限制集最初设置为所有权限。
代码后面是该示例的说明。
此示例的源代码也可以通过 Sun 下载中心获得。请访问 http://www.sun.com/download/products.xml?id=41912db5
1 #include <priv.h> 2 /* Always use the basic set. The Basic set might grow in future 3 * releases and potentially retrict actions that are currently 4 * unrestricted */ 5 priv_set_t *temp = priv_str_to_set("basic", ",", NULL); 6 /* PRIV_FILE_DAC_READ is needed in this example */ 7 (void) priv_addset(temp, PRIV_FILE_DAC_READ); 8 /* PRIV_PROC_EXEC is no longer needed after program starts */ 9 (void) priv_delset(temp, PRIV_PROC_EXEC); 10 /* Compute the set of privileges that are never needed */ 11 priv_inverse(temp); 12 /* Remove the set of unneeded privs from Permitted (and by 13 * implication from Effective) */ 14 (void) setppriv(PRIV_OFF, PRIV_PERMITTED, temp); 15 /* Remove unneeded priv set from Limit to be safe */ 16 (void) setppriv(PRIV_OFF, PRIV_LIMIT, temp); 17 /* Done with temp */ 18 priv_freeset(temp); 19 /* Now get rid of the euid that brought us extra privs */ 20 (void) seteuid(getuid()); 21 /* Toggle PRIV_FILE_DAC_READ off while it is unneeded */ 22 priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, NULL); 23 /* Toggle PRIV_FILE_DAC_READ on when special privilege is needed*/ 24 priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, NULL); 25 fd = open("/some/retricted/file", O_RDONLY); 26 /* Toggle PRIV_FILE_DAC_READ off after it has been used */ 27 priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, NULL); 28 /* Remove PRIV_FILE_DAC_READ when it is no longer needed */ 29 priv_set(PRIV_OFF, PRIV_ALLSETS, PRIV_FILE_DAC_READ, NULL);
该程序定义了名为 temp 的变量。temp 变量确定此程序不需要的权限集。最初在第 5 行,将 temp 定义为包含基本权限集。在第 7 行,将 file_dac_read 权限添加到 temp 中。proc_exec 权限对 exec(1) 新进程(在此程序中不允许)是必需的。因此,在第 9 行中从 temp 中删除了 proc_exec,从而使 exec(1) 命令无法执行新进程。
此时,temp 仅包含该程序所需的那些权限,即基本集加上 file_dac_read,再删除 proc_exec。在第 11 行中,priv_inverse() 函数将计算 temp 的逆向值,并将 temp 的值重置为补值。逆向值是从所有可能权限集中删除指定集(在本例中为 temp)所得的结果。作为第 11 行的结果,temp 现在包含该程序永不使用的那些权限。在第 14 行中,从允许集中删除了 temp 定义的不需要的权限。此删除操作还从有效集中有效地删除了这些权限。在第 16 行中,从限制集中删除了不需要的权限。在第 18 行中,因为不再需要 temp,因而释放了 temp 变量。
该程序可以识别权限。因此,该程序不使用 setuid,但可以将有效的 UID 重置为第 20 行中的用户的实际 UID。
在第 22 行中,通过从有效集中删除 file_dac_read 权限禁用了该权限。在实际的程序中,需要 file_dac_read 之前,还将发生其他活动。在该样例程序中,读取第 25 行中的文件需要 file_dac_read。因此,在第 24 行中,启用了 file_dac_read。读取文件后,将再次从有效集中立即删除 file_dac_read。读取所有文件后,通过在所有权限集中禁用 file_dac_read,可永久地删除 file_dac_read。
下表说明了随着程序的运行如何转换权限集。已指出了行号。
表 2–2 权限集转换
步骤 |
temp 集 |
允许权限集 |
有效权限集 |
限制权限集 |
---|---|---|---|---|
最初 |
— |
所有 |
所有 |
所有 |
第 5 行-将 temp 设置为基本权限 |
基本 |
所有 |
所有 |
所有 |
第 7 行-将 file_dac_read 添加到 temp 中。 |
基本 + file_dac_read |
所有 |
所有 |
所有 |
第 9 行-从 temp 中删除了 proc_exec。 |
基本 + file_dac_read - proc_exec |
所有 |
所有 |
所有 |
第 11 行-将 temp 重置为逆向值。 |
所有 -(基本 + file_dac_read - proc_exec) |
所有 |
所有 |
所有 |
第 14 行-在允许集中禁用不需要的权限。 |
所有 -(基本 + file_dac_read - proc_exec) |
基本 + file_dac_read - proc_exec |
基本 + file_dac_read - proc_exec |
所有 |
第 16 行-在限制集中禁用不需要的权限。 |
所有 -(基本 + file_dac_read - proc_exec) |
基本 + file_dac_read - proc_exec |
基本 + file_dac_read - proc_exec |
基本 + file_dac_read - proc_exec |
第 18 行-释放了 temp 文件。 |
— |
基本 + file_dac_read - proc_exec |
基本 + file_dac_read - proc_exec |
基本 + file_dac_read - proc_exec |
第 22 行-禁用 file_dac_read 直到需要时再启用。 |
— |
基本 - proc_exec |
基本 - proc_exec |
基本 + file_dac_read - proc_exec |
第 24 行-需要时启用 file_dac_read。 |
— |
基本 + file_dac_read - proc_exec |
基本 + file_dac_read - proc_exec |
基本 + file_dac_read - proc_exec |
第 27 行-执行 read() 操作后禁用 file_dac_read。 |
— |
基本 - proc_exec |
基本 - proc_exec |
基本 + file_dac_read - proc_exec |
第 29 行-不再需要 file_dac_read 时,从所有集中删除该权限。 |
— |
基本 - proc_exec |
基本 - proc_exec |
基本 - proc_exec |