[转载]unix环境高级编程备忘:理解保存的设置用户ID,设置用户ID位,有效用户ID,实际用户ID

转载自http://www.cnblogs.com/stemon/p/5287631.html

一、基本概念

实际用户ID(RUID):用于标识一个系统中用户是谁,一般是在登录之后,就被唯一的确定,就是登录的用户的uid。

有效用户ID(EUID):用于系统决定用户对系统资源的权限,也就是说当用户做任何一个操作时,最终看它有没有权限,都是在判断有效用户ID是否有权限。如果有,则ok,否则报错不能执行。在正常的情况下,一个用户登录之后(假设是A用户),A用户的有效用户ID和实际用户ID是相同的,但是如果A用户在某些场景中想要执行一些特权操作,能顺利的执行吗?上面说到了用户的任务操作,linux内核都是通过检验有效用户ID来判断当前执行这个操作的用户是否具有权限。这里的A用户想要执行的是特权操作,A用户没有这个权限,所以A用户就只能通过一定的手段来修改当前的有效用户ID使其具有执行特权操作的权限。

这里总结一句话:为什么要修改进程的有效用户ID,就是想在某一时刻能够执行一些特权操作。

设置用户ID位:用于对外的权限的开放,它的作用是修改进行的有效用户ID,给进程赋予临时的特权。

保存的设置用户ID:是有效用户ID副本,既然是有效用户ID的副本,那么它的作用肯定是为了以后恢复有效用户ID。

这里涉及很多的ID,通过下图看一下这些ID都是属于谁:

下面说一下文件设置用户ID位,这个ID仅仅是一个二进制的bit位,在文件stat结构的st_mode成员中,对于一般的文件,该位是置为无效的,只有可执行文件的该位是置为有效的。

二、改变三个用户ID的方法

下面这幅图给出了改变实际用户ID、有效用户ID和保存的设置用户ID的方法

再来一幅图看一下文件的ID和进程的ID在权限访问上的对应关系。对于一个普通文件,有三个ID,这三个ID对应三组权限,这三组权限控制着进程对该文件的访问权限。

在注意一点:ID并不是一个int或者一个标识,他是一个操作系统用户的标号,该用户创建的所有的进程都是这个ID。

对于linux系统,某个用户登录后,创建一个文件,那个这个文件的用户ID就是这个用户的ID。该用户创建的所有的进程都可以访问这个文件,因为该用户创建的进程的实际用户ID和有效用户ID都是这个用户的ID。但是当一个用户创建的进程要去访问其他用户创建的文件的时候,就需要用到有效用户ID的改变,来能够有权限访问这个文件。

1. 进程打开、创建、删除文件时的权限测试?

测试的参与者是进程的有效用户id、有效组id、文件的拥有者id、文件拥有者组id。

(1)进程的有效用户id是0(超级用户),则允许访问;

(2)进程的有效用户id等于文件拥有者id,则按照拥有者的权限访问;

(3)进程有效组id或附加组id之一等于文件拥有者组id,则按照文件拥有者组的权限访问;

(4)否则,按照文件的其他用户访问权限访问;

上述的四步是按续依次进行的。

2. 什么时候用到设置用户id和设置组id?

当进程通过exec函数执行某个文件的时候,而且文件的设置用户id只会影响到进程的有效用 户id

(其实也足够了,因为只有有效用户id参与权限测试),这样说不够严谨,因为进程的保存设置用

户id和保存设置组id会被exec函数从有效用户

id和有效组id复制过来,所以保存的设置用户id和保存的设置组id也会随之改变。

(1)当文件的设置用户id位和设置组id位没有打开:

进程的有效用户id和有效组id保持不变,严格按照第2步进行权限测试;

(2)文件的设置用户id位和设置组id位被打开:

exec函数才会把进程的有效用户id和有效组id设置为文件拥有者的用户id和组id,这时再进

行权限测试,进程就拥有了和文件拥有者一样的访问权限。

3. 进程保存设置用户id和保存设置组id有什么用?

顾名思义,这两个id存在的价值就是保存,保存谁呢?保存有效用户id和有效组id。当进程的

实际用户id和有效用户id不同时(组id同理),保存的设置用户id才有意义。因为这样就可以通

过调用setuid()把有效用户id切换为与进程的实际用户id或保存的设置用户id相同的值,不保存下

来,我们就弄丢了。

三、具体实例分析

实例分析一:

如何在权限不够的情况下执行特权权限,具体的方法就是更改进程的有效用户ID。

对于linux系统来说,用户的密码都存放在/etc/shadow文件下,查看一下这个文件的权限:

假如我是一个普通的用户,显然我是可以修改我自己的密码的,通过passwd命令,无可厚非,自己修改自己的密码当然是允许的。

但是仔细想想有没有什么不对的地方,作为一个普通的用户登录后,我的所有的进程的实际用户ID和有效用户ID都应该是我自己(这个用户)的UID。从上面对/etc/shadow文件用户ID和所属的组ID看,我不具备修改这个文件的权限,那么执行passwd命令是怎么修改我的密码的呢?

根据上面所讲的知识,决定进程对文件的访问权限的是执行操作时的有效用户ID,所以在执行passwd命令时,进行的有效用户ID肯定不是我这个普通用户的UID,一定被修改过了。

还有一点,在执行passwd这个命令的时候,在磁盘上肯定有一个可执行的文件,下面看看这个可执行文件的权限:

我们看到了一个s,这就是使用户设置ID位有效,上面说过这个位的作用就是修改执行这个可执行文件的进程的有效用户ID,那么我么来看看他是怎样修改执行passwd命令的进程的有效用户ID的。

首先看一下命令的执行的过程,当普通用户执行passwd命令时,shell会fork出一个子进程,此时该进程的有效用户ID还是这个普通用户的ID,然后exec程序执行/usr/bin/passwd这个例程(可执行文件)。通过上面的表我们就能知道,exec发现/usr/bin/passwd这个可执行文件有SUID为,于是会把进程的有效用户ID设置成可执行文件的用户ID,显示是root,此时这个进程就获得了root的权限,得到了读写/etc/shadow文件的权限,从而普通用户可以完成密码的修改。exec进程退出后,会恢复普通用户的有效用户ID为实际用户ID(也就是该普通用户的ID),这样就保证了不会是普通用户一直具有root权限。

在exec时,修改进程的有效用户ID为文件的用户ID,还是恢复进程的有效用户ID为进程的实际用户ID,这些都是linux内核完成的,用户是不能干预这些步骤的。

这样的过程既实现了普通用户权限的暂时的提升,不让普通用户长久的拥有root权限。同时即使在普通用户拥有root权限的这段短暂的时间里,普通用户也不能为所欲为,因为普通用户的这个进程必须按照/usr/bin/passwd这个可执行文件的指令内容执行,不能干之外的任何事情,而这个可执行文件又是root事先写好的,多么完美的设计啊~~

这就是设置用户ID为的作用,它的存在就是为了普通用户能够在某些时候获取一段时间的超级权限,但是你可能疑问,为什么不能用setuid直接修改呢?

如果这里可以用setuid直接修改进程的有效用户ID来获得特权权限,那么整个系统的超级权限就不可控制了,这违背了最小权限模型。所以linux的设计是:setuid在非特权用户下面,有效用户ID只能设置成为实际用户ID或者保存的设置用户ID。保存的设置用户ID又是有效用户ID的副本,有效用户ID只能是实际用户ID或者文件的所有者ID(在设置了保存用户ID位时)。

这样你就不能将有效用户ID设置成随意的值,所以对普通用户创建的任何文件,如果没有得到超级用户的授权,那么无论怎么编写代码来设置运行进程的有效用户ID或者设置用户ID位,由于这个可执行文件是普通用户自己编写的,所以权限根本没有任何改变。这里就是说只有root自己创建的可执行文件并且设置了用户ID位,才能够提升执行该可执行文件的进程的权限。

实例分析二:

上面的例子中没有用到保存的设置用户ID,看看这个ID的作用。既然保存的设置用户ID是有效用户ID的副本,那么肯定是为了在某个时刻用于恢复原先的有效用户ID的,这样就能够实现用户权限的切换。

例如:man命令(这是AUP上面的例子,当然实际linux上好像不是这么实现的,不过还是哪这个当做例子说明)

man程序文件的所有者和其它属组通常都是man自身保留的用户和组,man可能需要执行许多其它命令,以处理包含显示手册页的文件,为了防止被欺骗和重写错误文件,man在两种权限之间切换。运行man命令用户的权限,拥有man可执行文件用户的权限。下面列出其工作步骤:

(1)man拥有者是名为man的用户,且设置用户ID位已经设置。当我们exec此程序的时候,用户ID的情况是:


1

2

3

实际用户ID=我们的用户ID

有效用户ID=man  //设置用户ID位已经设置,有效用户ID改变

保存的设置用户ID=man

(2)进程访问需要的配置文件和手册页,这些文件由man用户拥有,因为有效用户ID是man,所以可以访问。

(3)man代表我们执行任何一个命令之前,调用setuid(getuid()),我们不是超级用户,所以仅仅改变有效用户ID,现在用户ID的情况变为:


1

2

3

实际用户ID=我们的用户ID(没有改变)

有效用户ID=我们的用户ID

保存的设置用户ID=man

看出来了吧,如果不保存下来,我们就把man用户丢了,再想setuid(man)就不会成功了,因为只允许将有效用户ID设置为与实际用户ID或者保存的设置用户ID相同的值。这时候,我们做回了自己。就只能访问我们通常可以访问的东西,没有特殊的权限了。man代表我们安全执行一次过滤。

(4)执行完过滤后,man再调用setuid(euid)(这里的euid是man的数值用户id,man自己通过geteuid获得并保存)。因为euid等于保存的设置用户ID,设置成功。

我们这时得到:


1

2

3

实际用户ID=我们的用户ID(未改变)

有效用户ID=man

保存的设置用户ID=man(未改变)

(5)因为有效用户ID是man,所以现在可以对其他文件进行操作了。  

四、新文件和新目录的所有权

1. 新文件的用户ID设置为进程的有效用户ID

2. 新文件的组ID有两种情况:

  • 新文件的组ID为进程的有效组ID
  • 如果新文件的组ID所在的目录的设置组ID位设置了的话,新文件的组ID将设为目录的组ID。

3. 其他

  • mkdir函数自动的传递了目录的设置组ID位
  • access函数是用设计用户ID和实际组ID来进行访问权限的测试的,如果你只是想测试一下实际用户所拥有的权限,这个函数就可以了。

五、编程应用

1. 在执行exec前后,进程的实际用户ID和实际组ID不变,而有效ID取决于所执行的程序文件是否设置了设置用户ID位和设置组ID位, 如果设置了设置用户ID位,则有效用户ID变成程序文件所有者ID,否则有效用户ID不变。对组ID处理方式与此相同
2. 保存的设置用户ID是由exec复制有效用户ID后得来的,所有说设置用户ID是有效用户ID的副本!
3. 一般设计应用程序时,总是试图使用最小特权模型,依照模型,程序应当只具有为完成任务所需的最小特权.
4. 使用函数:


1

2

3

4

5

6

7

8

#include <unistd.h>

int setuid(uid_t uid); //设置实际用户ID和有效用户ID,如果进程为超级用户,则会设置实际、有效、保存的设置用户ID

int setgid(gid_t gid); //设置实际组ID和有效组ID

int setreuid(uid_t ruid, uid_t euid); //交换实际用户ID和有效用户ID

int setregid(gid_t rgid, gid_t egid);

int seteuid(uid_t uid); //更改有效用户ID

int setegid(gid_t gid);

注意: 
根据书上来看,特权用户使用这几个函数的时候,都是直接用参数的值来设置实际用户ID或者有效用户ID,这些值都可以是任意的。
例如:setreuid(ruid, euid), 如果是特权用户,则直接设置实际用户ID为ruid,有效用户ID为euid。

但非特权用户就不行了,非特权用户用setuid(),seteuid()则只能将有效有户ID设置为实际用户ID或保存的设置用户ID,如果不是这两个数,设置失败!

对编程应用方面的总结:
非特权用户不能指定任意的有效用户ID, 结合其他几个函数来交换或设置有效用户ID, 与设置用户ID位一起实现权限的控制。

原文地址:https://www.cnblogs.com/J1ac/p/8893840.html

时间: 2024-10-11 10:05:45

[转载]unix环境高级编程备忘:理解保存的设置用户ID,设置用户ID位,有效用户ID,实际用户ID的相关文章

(十一) 一起学 Unix 环境高级编程 (APUE) 之 高级 IO

. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制 (七) 一起学 Unix 环境高级编程 (APUE)

unix环境高级编程——如何看英文书籍

这本书还没看完,决定暂时先放下,剩下的部分觉得可以以后有时间再看. 我觉得还需要以后再看的部分有: 一是Chapter 14 Advanced I/O,比如record locking,我记得应该是在这章里介绍的.还有Chapter 17 Advanced IPC.这些确实是比较高级的话题,留着以后看不是因为现在用不着,而是我想先继续下面的学习了--刚拍了一本<Linux设备驱动程序>.这两章我觉得也是比较能修改内功的章节,并不是一定要用到时再去翻翻. 当然除此之外还有其它未看的章节,不作一一

Unix 环境高级编程 (APUE) 之 网络 IPC:套接字

一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字 . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APU

UNIX环境高级编程笔记之文件I/O

一.看图说话 一图胜过千言,看图! 二.唠一唠 在写之前,先唠几句,<UNIX环境高级编程>,简称APUE,这本书简直是本神书,像我这种小白,基本上每看完一章都是“哇”这种很吃惊的表情.其实大概三年前,那会大三,我就买了这本书,也看过一些,但好像没有留下什么印象,今天再看,依然觉得像新的一样.很大的原因我想是一直以来都在用windows(用windows做开发为什么学不到真正的技术,我想大家都懂的),当然知识结构不完整,学习能力这些就不说了.所以,对于那些致力于想在Linux下做开发的人来说,

《UNIX环境高级编程》学习心得 一

本文内容大部分摘自<UNIX环境高级编程>,附有部分个人心得. 1.unix体系结构 从严格意义上来说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境.我们通常将这种软件称为内核(kernel),因为它相对较小,而且位于环境核心.如图显示unix体系结构. 内核接口被称为系统调用(system call).公用函数库构建在系统调用接口之上,应用程序既可食用公用函数库,也可以使用系统调用.shell是一个特殊的应用程序,为运行其他应用程序提供了一个接口. 从广义上来讲,操作系

OS X下UNIX环境高级编程(第三版)学习日志-第一章ChapterI,编译apue包与第一个例程

1.从网络上获取代码,地址如下apue最新官方下载地址 2.解压到本地 由于最新版本是第三版(3rd Edition),apue.3e,就是我们要的源代码 3.编译源代码 编译过程中笔者并未遇到任何问题,所以建议大家也下载最新版本的代码来学习,贴一下笔者的环境信息 Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 Apple

【转】apue《UNIX环境高级编程第三版》第一章答案详解

原文网址:http://blog.csdn.net/hubbybob1/article/details/40859835 大家好,从这周开始学习apue<UNIX环境高级编程第三版>,在此,我要感谢网易的一个工程师朋友和室友,没有他们,我不会开始真正的学习这本书,希望大家以后开始慢慢进步.废话少说,直接上课后习题了. UNIX高级编程第一章习题答案: 1.1在系统上验证,除根目录外,目录l和l l是不同的. 答:这个验证有很多方法可使用命令ls .cd.vim等,目录.指向当前目录,目录..指

《Unix环境高级编程》读书笔记 第7章-进程环境

1. main函数 int main( int argc, char *argv[] ); argc是命令行参数的数目,包括程序名在内 argv是指向参数的各个指针所构成的数组,即指针数组 当内核执行C程序时(使用exec函数),在调用main前先调用一个特殊的启动例程.可执行程序文件将此启动例程指定为程序的起始地址——这是由连接器设置的,而连接器则是由C编译器调用.启动例程从内核取得命令行参数和环境变量值,然后按上述方式调用main函数做好安排. 2. 进程终止 有8种方式使进程终止,其中5种

《UNIX环境高级编程(第3版)》

<UNIX环境高级编程(第3版)> 基本信息 原书名:Advanced Programming in the UNIX Environment (3rd Edition) (Addison-Wesley Professional Computing Series) 原出版社: Addison-Wesley Professional 作者: (美)W. Richard Stevens    Stephen A. Rago 译者: 戚正伟 张亚英 尤晋元 出版社:人民邮电出版社 ISBN:9787