Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1478109
  • 博文数量: 842
  • 博客积分: 12411
  • 博客等级: 上将
  • 技术积分: 5772
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-14 14:43
文章分类

全部博文(842)

文章存档

2013年(157)

2012年(685)

分类: 系统运维

2012-05-14 15:28:20



在UNIX系统里,权限,比如当前日期的系统标记,和访问控制,比如读写一个特殊文件,都基于用户和组ID。当我们的程序需要额外的权限或需要访问它们当 前不被允许访问的资源,它们需要改变它们的用户或组ID成为一个有合适权限或访问的ID。相似地,当我们的程序需要降低它们的权限或阻止特定资源的访问, 那么它们通过改变它们的用户ID或组ID成一个没有权限或资源访问能力的ID来完成。


一般说来,我们尝试使用最低权限模式,当我们设计我们的应用程序的时候。根据这个模式,我们的程序应该使用完成任何任务所需的最低权限。这会降低恶意用户尝试用它们的权限用意想不到的方式欺骗我们程序来破坏安全的可能性。


我们可以用setuid函数来设置真实用户ID和有效用户ID。相似地,我们可以用setgid函数设置真实组ID和有效组ID。



  1. #include <unistd.h>

  2. int setuid(uid_t uid);

  3. int setgid(gid_t gid);

  4. 两者成功都返回0,失败返回-1.


有些为那些能改变ID的人准备的规则。我们现在只考虑用户ID。(所有我们为用户ID的描述同样适用于组ID。)


1、如果进程有超级用户权限,那么setuid函数设置真实用户ID、用效用户ID和保留的设置用户ID。


2、如果进程没有超级用户权限,但是uid等于真实用户ID或保存的设置用户ID,那么setuid只把有效用户ID设置为uid。真实的用户ID和保存的设置用户ID不会改变。


3、如果这两个条件没有一个成立,那么errno被设为EPERM,并返回-1.


这里,我们假设_POSIX_SAVED_IDS为真。如果这个特性没有被支持,那么删除之前所有对保存的设置用户ID的描述。


保存的ID不是POSIX.1的2001版本里必需的特性。它们常是POSIX早期版本的可先项。要看一个实现是否支持这个特性,一个应用可以在编译器测试常量_POSIX_SAVED_IDS,或者在运行时用_SC_SAVED_IDS参数调用syncof。


我们可以给出关于内核维护的这三个用户ID的描述:


1、只有一个超级用户进程可以改变真实用户ID。通常,真实用户ID在我们登录是由login程序设置并不再改变。因为login是一个超级用户进程,所以当它调用setuid时它设置所有的三个用户的ID。


2、有效用户ID由exec函数设置,仅当程序文件的设置用户ID被设置。如果设置用户ID位没有被设置,那么exec函数不改变有效用户ID,继续保持 当前值。我们可以在任何时间调用setuid来把有效用户ID设置为真实用户ID或保存的用户ID。自然地,我们不能把有效用户ID设置为任何随机值。


3、保存的设置用户ID通过exec函数由有效用户ID拷贝而来。如果文件的设置用户ID位被设置,那么这个拷贝在exec从文件的用户ID存储有效用户组ID之后被保存。


下表总结了这三个ID可能被改变的各种方式:

改变三个用户ID的方法
ID exec setuid(uid)
设置用户ID位关 设置用户ID位开 超级用户 没有特权的用户
真实用户ID 不变 不变 设置为uid 不变
有效用户ID 不变 从程序文件的用户ID拷贝 设置为uid 设置为uid
保存的设置用户ID 从有效用户ID拷贝 从有效用户ID拷贝 设置为uid 不变

注意我们可以用8.2节的函数getuid和geteuid来得到真实用户ID和有效用户ID的当前值。我们不能得到保存的设置用户ID的当前值。

例:为了看到保存的设置用户ID特性的工具,让我们检查下使用它的程序的操作。我们将看到man程序,它被用来显示在线手册页。man程序可以被安装为它 的设置用户ID或设置组ID被设为指定的用户或组,通常是由man为它本身预留的一个。man程序可以被用来读和可能地写文件,在我们通过一个配置文件 (通常为/etc/man.config或/etc/manpath.confg)或使用一个命令行选项来选择的位置。


man程序可能必须执行几个其它命令来处理包含要显示的手册页的文件。为了避免被欺骗以致运行错误的命令或覆写错误的文件,man命令必须在两个权限集里交换:运行man命令的用户和拥有man可执行文件的用户。下面的步骤会发生:


1、假设man程序文件被用户名man拥有并设置了设置用户ID,当我们exec它时,我们有:真实用户ID=我们的用户ID;有效用户ID=man;保存的设置用户ID=man。


2、man程序访问所需的配置文件和手册页。这些文件被用户名man拥有,但是因为有效用户ID是man,所以文件访问被允许。


3、在man为我们执行任何一个程序时,它调用setuid(getuid())。因为我们不是超级用户进程,所以这只改变有效用户ID。我们有:真实用 户ID=我们的用户ID(不变);有效用户ID=我们的用户ID;保存的设置用户ID=man(不变)。现在man进程用我们的用户ID作为它的有效用户 ID运行。这意味着我们只能访问我们有普通权限的文件。我们没有更多的权限。它可以为我们执行任何过滤器。


4、当过滤完成时,man调用setuid(euid),这里euid是用户名man的数值用户ID。(这是我们为什么需要保存的设置用户ID。)现在我们有:真实用户ID=我们的用户ID(不变);有效有用ID=man;保存的设置用户ID=man(不变)。


5、man程序现在可以在它的文件上进行操作,因为它的用效用户ID是man。


通过用这种方式使用保存的设置用户ID,我们可以使用在进程开始和结束时由设置用户ID赋予的额外的权限。然而,在这期间,进程用我们的通过权限来运行。 如果我们不能在最后切换回保存的设置用户ID,那么我们可能会在我们运行的整个时间里都保持额外的权限(这会导致问题)。


让我们看到下如果man在运行时为我们产生一个shell会发生什么。(这个shell是用fork和exec产生的。)因为真实用户ID和有效用户ID 都是普通用户ID(第3步),所以shell没有额外的权限。当man运行时,shell不能文件设置为man的设置用户ID,因为shell的保存的设 置用户ID被exec从有效用户ID拷贝过来。所以在执行exec的子进程里,所有的三个用户ID都是我们的普通用户ID。


我们关于man如何使用setuid函数的描述不正确,如果程序的设置用户ID为根用户,因为一个用超级用户权限的setuid的调用会设置三个用户ID。为了上面的例子工作,我们需要setuid来仅设置有效用户ID。


setreuid和setregid函数


历史上,BSD支持使用setreuid函数来交换真实用户ID和有效用户ID。



  1. #include <unistd.h>

  2. int setreuid(uid_t ruid, uid_t euid);

  3. int setregid(gid_t rgid, gid_t egid);

  4. 两者成功返回0,错误返回-1.


我们可以为任何参数返回-1的值来指定对应ID应该保持不变。


规则很简单:一个没有权限的用户总是可以在真实用户ID和有效用户ID之间交换。这允许一个设置用户ID程序来交换用户的普通权限并在之后为设置用户ID 交换回来。当这个保存的设置用户ID特性在POSIX.1被引入时,规则被增强为同样允许一个无权限的用户来把它的有效用户ID设置为保存的设置用户 ID。


setreuid和setregid都是SUS里的XSI扩展。这样,所有UNIX系统实现都应该提供对它们的支持。


4.3BSD没有之前讨论的保存的设置用户ID特性。相反它使用setreuid和setregid。这允许一个无权限的用户来在这两个值之间切换来切换 去。然而,注意当使用这个特性的程序产生一个外壳时,它们必须把真实用户ID在exec之前设置为普通用户ID。如果我们不能这样做,真实用户ID可能被 授权(由setreuid完成的交换而来)而且外壳进程可能调用setreuid来交换这两者来假设更高权限用户的权限。作为一个解决这个问题的健壮的程 序,程序在子进程里exec调用之前设置真实用户ID和有效用户ID两者。


seteuid和setegidbiov


POSIX.1包含了两个函数seteuid和setegid。这些函数和setuid和setgid相似,但是只改变有效用户ID和有效组ID。



  1. #include <unistd.h>

  2. int seteuid(uid_t uid);

  3. int setegid(gid_t gid);

  4. 两者成功都返回0;错误返回-1.


一个没有特权的用户可以把它的有效用户ID设置为真实用户ID或它的保存的设置用户ID。对于一个特权用户,只有有效用户ID被设为产学研。(这和setuid函数不同,它改变所有三个用户ID)。


下面总结了所有我们这里讨论过的修改这三个用户ID的函数:


超级用户:setreuid(ruid, euid)设置真实用用户ID和有效用户ID;setuid(uid)设置真实用户ID、有效用户ID和保存的设置用户ID;seteuid(uid)设置有效用户ID。


普通用户:setuid或seteuid把真实用户ID或保存的设置用户ID设置为有效用户ID;setreuid交换真实用户ID和有效用户ID或把保存的设置用户ID设置为用效有户ID。


设置用户ID程序的exec:同时设置有效用户ID和保存的设置用户ID。


组ID

我们在本节至今说过的所有事同样也以相似的方式应用于组ID。补充组ID不被setgid、setregid或setegid影响。


阅读(799) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~