第八章(二) exec系列函数 和 进程的几个ID的认识

来源:互联网 发布:淘宝买星空连门卡 编辑:程序博客网 时间:2024/06/07 03:43

setbuf(stdout,NULL) 即将输出缓冲关闭了。



函数     exec

之前对这个的理解就是在进程fork后调用,是会覆盖fork出来的程序段的。除了环境变量、打开的文件描述符等不会变,其他就是面目全非了。之前在
          写一个非常非常简陋的shell的时候就是用的这个。 exec可不是函数,只是一个函数的系列。

当进程调用一种exec函数时,该进程执行的程序完全替换为新程序。而新程序则从其main函数开始执行。
         exec函数只是用磁盘上的一个新程序替换来当前进程的正文段,数据段、堆段 和 栈段
         以下为 exec 系列函数:    (函数名后有p则表示会在PATH中寻找, v表示vector 矢量, l则表示list , 以下的arglish必须以 NULL 做结尾

        execvp (file , arglist)                           //    execvp("ls",arglist)        execv(fullpath , arglist)                        //     execv("/bin/ls" , arglist)        execlp(file , argv0 , argv1 , ... ,NULL);       //     execlp("ls","ls","-a","/home",NULL)          execl(fullpath , argv0 , argv1 , ... ,NULL);    //     execl("/bin/ls","ls","-a","/home",NULL)
用到file的即为直接输入程序的名字,会自己在PATH变量中寻找该file。
用到fullpath的则为直接通过完整的文件名得到该程序,会快些也更准确些
execve 和 execle 则是会选取最后一个参数作为自己的新环境而不使用父进程的环境



关于命令参数
每个系统对参数表和环境表的总长度都有一定的限制。是由  ARG_MAX给出的,做一些操作时候可能会受到此影响
        为了摆脱对参数表长度的限制,我们可以使用 xargs 命令,将长参数表断开成几部分。

        比如在man手册中搜索关于getrlimit的部分则是:

find /usr/share/man -type f -print | xargs grep getrlimit-type f 是指在普通文件中搜索  , 使用了 xargs 就表示 grep 在一段参数表中搜索后在下一段参数表中搜索 。。。 不至于参数表太长出错

使用exec后对父进程打开文件的处理与 每个文件描述符的 执行时关闭(close-on-exec)标志有关。 若打开来该标志,则在执行exec时关闭该文件描述符,否则
        文件描述符仍打开。  默认使将该标志关掉的,即在exec后是依旧打开描述符的。
学到这里,发现自己 对于 find 和 xargs 太生疏了, 因此转来一篇关于 find 和 xargs的文章,作为知识的补充。
        http://blog.csdn.net/u012062760/article/details/42710775




更改用户ID 和 更改组ID    (这一部分结合之前 谈到的 SUID 和 SGID 一起理解)

我们为什要要更改 ID 呢?
        一般而言,在设计应用时,我们总是试图使用 最小特权 模型,依照此模型 ,我们的程序应当只具有为完成给定任务所需的最小特权。
        这降低了恶意用户试图哄骗程序以未预料的方式使用特权造成的安全性风险。

如何修改?
        使用 setuid 设置用户的实际ID 和 有效用户ID。 
        使用 setgid 设置实际组ID 和 有效组ID


那什么是 real user ID ,什么又是 effective user ID 呢? 用来干嘛的呢 ? saved set-user-ID(保存的设置用户ID) 又是什么 ?
        real user ID表示的是实际上进程的执行者是谁
        effective user ID主要用于校验该进程在执行时所获得的文件访问权限
                     也就是说当进程访问文件时检查权限时实际上检查的该进程的"effective user ID"
        saved set-user-ID 仅在 超级用户操作下 或 程序设置 SUID 位之后发生改变.(这里仅写下如何发生改变的,下文指出该ID用处)

更改用户ID 的规则:     setuid(uid)

        一、若进程具有超级用户的权限, 则 setuid 函数将实际用户 ID、 有效用户 ID 以及保存的设置用户 ID 设置为 参数 uid。
        二、若进程没有超级用户权限, 但是 uid等于实际用户ID 或保存的设置用户ID ,则setuid 只将有效用户ID设置为 uid。不更改实际用户ID和保存的设置用户ID
        三、如果上面两个条件都不能满足, 则 errno 设置为 EPERM,返回 -1.


看到这里是否乱了 ?  现在来看看什么是 saved set-user-ID 呢?四处查找后,对此有了个模糊的概念:
先来一个自己的理解:
前提:

如果一个进程是以普通用户身份来运行的,那么real 和 effective 这两个 ID 一般来说是相同的,并且也不能随便修改。
只有一种情况例外:此进程的可执行文件的权限标记中,设置了“SUID”位!
这样就可以改变进程的(或者说exec函数才设置) effective ID了(根据前文,saved set-user-ID也就变了)
设置一个可执行文件的“SUID”位的最简单的方法,就是用  chmod +s /path/to/file

 举例:

假设现在有 A 、B 两个账户, 现在B 创建了一个可执行程序 prog.(这个程序已经设置了SUID位, 即执行该程序后 effective ID 为文件的owner)
现在让A 去执行这个程序,对于这个程序来说, 它的real user ID 就是A, 它的effective user ID 就是B, 它的 saved set-user-ID就是B。
好了,进程执行了一会儿,想执行自己的文件了, 那么就要改变effective user id 改为自己的 id ,即 setuid(getuid()).
又过了一会儿,进程又要执行本程序owner的文件了 ,那我怎么改回来呢 ,不是root的话是不能乱改ID的啊 ?
现在就是使用 saved set-user-id的时候了,前文更改用户ID 的规则第二点说道:
若进程没有超级用户权限, 但是 uid等于实际用户ID 或保存的设置用户ID ,
 则setuid 只将有效用户ID设置为 uid。不更改实际用户ID和保存的设置用户ID
并且我们之前不是用saved set-user-id来保存来 程序owner的id了么,且并未在进程过程中发生改变,我们就能使用这个ID来切换回去了!
这就是 saved set-user-id的大致的用途了。

(不过实际过程中并没有saved set-user-id这个变量,也就是没有东西帮你记下ID,需要你自己找到这个用户(owner)的ID 但是)

(哇擦, 发现有一个函数可以得到这3个ID 的   名字叫          getresuid)


-----------------------------------------

   
这时候发现书上有部分专门对这个saved set-user-id作出了解释:
是拿 at 作为例子, 这个命令是用来调度将来某个时刻要运行的命令
        为了防止被欺骗而运行不被允许的命令或读、写没有访问权限的文件,at命令和最终代表用户运行命令的守护进程必须在两种特权之间切换:

用户特权 和 守护进程特权

下面列出工作步骤:

            
 一、 程序文件为 root 所有, 且 SUID 位已设置。当我们运行此程序
进程的real user ID 就是 我们自己, 它的effective user ID 就是 root , 它的 saved set-user-ID就是 root。

二、 at 程序做的第一件事就是降低特权,以用户特权运行。它调用 setuid函数把有效用户id设置为实际用户id,此时:
进程的real user ID 就是 我们自己, 它的effective user ID 就 为我们自己 , 它的 saved set-user-ID就是 root。

三、 at 程序以我们的用户特权运行,直到它需要访问控制哪些命令即将运行,这些命令需要何时运行的配置文件时,at程序的特权会改变,因为这些文件被为用户
运行命令的守护进程所持有。 at 调用setuid将euid设置为 root ,因为setuid的参数等于 saved set-user-id, 所以这种调用是允许的。

(这就是用到这个 saved set-user-id 的原因了) 此时:
进程的real user ID 就是 我们自己, 它的effective user ID 就是 root , 它的 saved set-user-ID就是 root。

四、 修改完配置文件后,at 调用 seteuid 把euid设置为 用户uid,降低其特权,防止对特权的误用。此时
进程的real user ID 就是 我们自己, 它的effective user ID 就 为我们自己 , 它的 saved set-user-ID就是 root。

五、 守护进程开始用 root 特权运行, 代表用户运行命令 ,守护进程调用 fork, 子进程调用 setuid 将它的用户ID更改至我们的用户 ID。
因此子进程以 root 特权运行, 更改来所有ID。 此时 子进程:
进程的real user ID 就是 我们自己, 它的effective user ID 是我们自己 , 它的 saved set-user-ID 是我们自己。

现在守护进程可以安全的代表我们执行命令,且只能访问我们能访问的文件,我们没有额外的权限。



关于内核维护的这3个ID,还需注意:

一、只有超级用户可以更改 实际用户ID。通常它是用户登录时 login程序设置的,且绝不会改变它。
            login是root进程,调用 setuid 设置了这 3个ID

二、saved set-user-id是由 exec 复制有效用户ID得到的。 若设置了 SUID 位,那么exec 根据文件owner ID设置进程有效用户ID后,这个副本就被保存起来了。
    以下再结合一张书上的图片来加强了解:


函数     setreuid(ruid, euid)
        可用于交换实际ID 和 有效ID 的值    我自己试了一下,并没有成功,若改换ID那不是换成别的user了? 不知到哪里出了问题
        不过在实验过程中倒是好像对权限问题迷迷糊糊了 。。。  这里再做下记录:
         只要有x权限就能运行,但是运行过程中的 real uid 和 euid 是运行程序的用户,不能使用程序owner的文档。 但是有来suid后,就可以对程序owner的文档进行操作。
         因为往往程序会操作一些文档,所以有时候如果没有对文档的权限,那么程序也是不能运行下去的
        若其中任一参数的值为 -1 ,则表示相应ID 应保持不变。


函数    seteuid 和 setegid
        一个非特权用户可以将其effective user id 设置为uid或者设置用户id
0 0