Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5513652
  • 博文数量: 922
  • 博客积分: 19333
  • 博客等级: 上将
  • 技术积分: 11226
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-27 14:33
文章分类

全部博文(922)

文章存档

2023年(1)

2020年(2)

2019年(1)

2017年(1)

2016年(3)

2015年(10)

2014年(17)

2013年(49)

2012年(291)

2011年(266)

2010年(95)

2009年(54)

2008年(132)

分类: LINUX

2012-01-15 20:43:59

++++++APUE读书笔记-08进程控制(08)++++++

 

11、修改用户id(UID)和组id(GID)
================================================
 在unix中,特权以及访问控制是基于UID和GID的。我们设计程序一般要使用最少的特权来完成我们的工作。
 我们可以使用如下函数设置用户的real user ID和effective user ID:
 #include
 int setuid(uid_t uid);
 int setgid(gid_t gid);

 修改这些ID有一套规则,我们以 UID为例(GID是一样的):
 a)如果进程有superuser特权,那么setuid函数会设置real user ID,effective user ID和saved set-user-ID为uid.
 b)如果进程没有superuser特权,但是设置的uid和real user ID或者saved set-user-ID相等,那么setuid仅仅将effective user ID设置为uid,real user ID和saved set-user-ID不变.
 c)如果以上两个条件都不满足,那么会返回1,并设置errno为EPERM.
 (这里设置的是进程的相应uid不是文件的,进程的三种uid:real uid, effective uid,saved set-user-id.核心是进程的effective uid)
 这里,我们假设_POSIX_SAVED_IDS是true.如果这个特性没有被提供,那么就删除前面所有和saved set-user-ID相关的内容。
 saved IDs是2001版本POSIX.1的必有特性。以前版本的POSIX它们是可选的。如果想要查看当前系统实现是否支持这个特性,我们可以在编译的时候测试_POSIX_SAVED_IDS宏开关,或者在运行的时候调用传递了_SC_SAVED_IDS的sysconf函数。

 我们简单描述一下内核是如何来维护这三种UID的:
 a)只有superuser进程可以改变real user ID.一般来说,real user ID是由login程序在我们登陆之时来设置,之后就不会改变了。因为login是一个superuser process,当它调用setuid的时候,它会设置所有三种user ID.
 b)effective user ID是exec函数在程序文件的set-user-ID位被置位的时候设置的(设置成程序文件的UID)。如果set-user-ID位没有被设置,那么exec函数保持当前的effective user ID值不变。我们可以在任何时候调用setuid设置effective user ID为real user ID或者saved set-user-ID.一般来说,我们不能把effective user ID设置成任何值。
 c)saved set-user-ID是exec从effective user ID拷贝来的。如果文件的set-user-ID位被设置,那么这个拷贝(saved set-user-ID)会在exec从文件的user ID存储到effective user ID之后被保存。
 这个参考资料中有个表格简单列出了这三种进程ID改变的情况如下:
+-----------------------------------------------------------------------------+
|    ID     |                 exec                 |       setuid(uid)        |
|           |--------------------------------------+--------------------------|
|           | set-user-ID bit  |  set-user-ID bit  | superuser | unprivileged |
|           |       off        |        on         |           |     user     |
|-----------+------------------+-------------------+-----------+--------------|
| real user | unchanged        | unchanged         | set to    | unchanged    |
| ID        |                  |                   | uid       |              |
|-----------+------------------+-------------------+-----------+--------------|
| effective | unchanged        | set from user ID  | set to    | set to uid   |
| user ID   |                  | of program file   | uid       |              |
|-----------+------------------+-------------------+-----------+--------------|
| saved     | copied from      | copied from       | set to    | unchanged    |
| set-user  | effective user   | effective user ID | uid       |              |
| ID        | ID               |                   |           |              |
+-----------------------------------------------------------------------------+

 注意,我们通过getuid和geteuid只能获得当前的real user ID和effective user ID.我们无法获得当前saved set-user-ID的值。

 例子:
 我们用一个例子来说明这个saved set-user-ID特性的使用。这个例子就是man程序。man程序可以用来显示在线帮助手册,man程序可以被安装指定set-user-ID或者set-group-ID为一个指定的用户或者组。man程序可以读取或者覆盖某些位置的文件,这一般由一个配置文件(通常是/etc/man.config或者/etc/manpath.config)或者命令行选项来进行配置。
 man程序可能会执行一些其它的命令来处理包含显示的man手册页的文件。为防止处理出错,man会从两个特权至今进行切换:运行man命令的用户特权,以及man程序的拥有者的特权。大致过程如下:
 a,假设man程序文件被用户man所拥有,并且已经被设置了它的set-user-ID位,当我们exec 它的时候,我们有如下情况:
 real user ID = 我们的用户UID
 effective user ID = man用户UID
 saved set-user-ID = man用户UID
 b.man 程序会访问需要的配置文件和man手册页。这些文件由man用户所拥有,但是由于effective user ID是man,文件的访问就被允许了。
 c,在man为我们运行任何命令的时候,它会调用setuid(getuid())).因为我们不是superuser进程,这个变化只能改变effective user ID. 我们会有如下情况:
 real user ID = 我们的用户UID(不会被改变)
 effective user ID = 我们的用户UID
 saved set-user-ID = man 的用户UID(不会被改变)
 现在man进程运行的时候把我们得UID作为它的effective user ID.这也就是说,我们只能访问我们拥有自己权限的文件。也就是说,它能够代表我们安全地执行任何filter.
 d.当filter做完了的时候,man会调用setuid(euid).这里,euid是man用户的UID.(这个ID是通过man调用geteuid来保存的)这个调用是可以的,因为setuid的参数和saved set-user-ID是相等的。(这也就是为什么我们需要saved set-user-ID).这时候我们会有如下情况:
 real user ID = 我们的用户UID(不会被改变)
 effective user ID = man的UID
 saved set-user-ID = man 的用户UID(不会被改变)
 e.由于effective user ID是man,现在man程序可以操作它自己的文件了。

 通过这样使用saved set-user-ID,我们可以在进程开始和结束的时候通过程序文件的set-user-ID来使用额外的权限。然而,期间我们却是以我们自己的权限运行的。如果我们无法在最后切换回saved set-user-ID,我们就可能会在我们运行的时候保留额外的权限。
 我们来看看如果man启动一个shell的时候会发生什么.(shell是使用fork和exec来启动的)因为real user ID和effective user ID都是我们的普通用户UID(参见step3).shell 没有其它额外的权限.shell无法访问saved set-user-ID(man),因为shell的saved set-user-ID是由exec从effective user ID拷贝过来的。所以,在执行exec的子进程中,所有的user ID都是我们的普通用户ID.
 实际上,我们描述man使用setuid函数的方法不是特别正确,因为程序可能会set-user-ID为root.这时候,setuid会把所有三种uid都变成你设置的id,但是我们只需要设置effective user ID.

 *关于setreuid和setregid函数
 以前,BSD支持用seteuid进行real user ID和effective user ID的切换。
 #include
 int setreuid(uid_t ruid, uid_t euid);
 int setregid(gid_t rgid, gid_t egid);
 两个函数如果成功则返回0,如果错误则返回1(实际值一般为-1)。
 如果任何一个参数设置为1,那么表示相应得ID保持不变。
 这个函数执行的规则很简单:未授权的用户可以切换real user ID和effective user ID.这允许一个set-user-ID程序切换到普通用户权限,然后又切换回set-user-ID权限。当saved set-user-ID特性从POSIX.1中引入的时候,这条规则变成了也允许一个非授权用户把它的effective user ID设置成saved set-user-ID.
 /*seteuid和setregid都是Single UNIX Specification中的XSI扩展。这样,所有UNIX系统都应该支持它们。*/
 4.3BSD没有saved set-user-ID特性,它使用setreuid和setregid来替代。这允许一个非授权用户在两个值之间来回切换。然而,当程序启动一个shell的时候,它需要在exec之前把real user ID设置成为normal user ID.如果不这样作,那么real user ID将会被授权了(从setreuid),然后shell进程可以调用seteuid来切换到更高用户权限。为了防止这种情况发生,需要在子进程中调用exec之前把real user ID和effective user ID设置成normal user ID。

 一个非授权用户可以设置它的effective user ID为real user ID或者它的saved set-user-ID.对于授权用户,只有effective user ID被设置成为了uid(这一点和setuid函数不同,setuid会把所有三个user ID改变)。
 参考资料最后有个图,给出了这些修改user ID函数的动作情况。这里省略了,图形描述的内容大致是,
 对于superuser,
 setreuid会修改real user ID和effective user ID为其参数所指定的。
 setuid会修改real user ID和effective user ID以及saved set-user-ID三者为其指定的参数。
 seteuid仅修改effective user ID为其参数所指定的。
 对于非特权用户,
 setreuid
 setuid会修改effective user ID的值,这个值可以为real userID或者saved set-user-ID.
 seteuid会修改effective user ID的值,这个值可以为real userID或者saved set-user-ID.
 另外,如果文件具有set-uid设置(也就是ls文件时候的rwx权限中的x变成s),那么exec的时候会把effectived user ID变成设置为文件属主,否则effectived user ID保持不变;saved set-user-ID是被exec从effectived user ID拷贝过来的,如果文件的set-user-ID被设置,那么在exec把effectived user ID设置成文件属主之后,再将effectived user ID拷贝一份存到saved set-user-ID中去。

 对于组gid来说,遵循的规则和上面用户uid的规则一样,但是"额外组"不受这些组id函数设置的影响。

参考:

 

 

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

vaqeteart2020-05-14 14:16:31

to org

vaqeteart2012-02-19 17:52:26

这一章很关键,是理解文件、进程用户权限的关键。