zoukankan      html  css  js  c++  java
  • SUID或SGID程序中能不能用system函数

      system()函数的声明和说明如下:

      

      注意它的描述那里,system()执行一个由command参数定义的命令,通过调用/bin/sh -c命令来实现这个功能。也就是说它的逻辑是这样的!

      进程调用system函数,system函数调用fork创建一个子进程,然后再调用exec函数来把这个子进程的正文段替换成/bin/sh命令的正文段。然后再由sh来执行exec将程序的正文段替换成command参数所代表的命令的正文段,例如,我的一个程序a.out来调用system函数来执行sleep 20命令,它的进程示意图如下所示:

      

      可以参考下面这个例子,如下图所示:

      这里我执行了一个system文件,产生了两个进程,3994和3995(右边那个终端所示,第一列是PPID,第二列是PID),这两个进程是父子关系,值得注意的是这两个进程进程ID是连着的,也就是说在这两个进程执行的时候没有新的进程产生。

      接下来开始扯正题,在《APUE》(UNIX环境高级编程,下文不再赘述)的8.13节中,作者强调SUID和SGID程序中不应该调用system函数。为什么呢,我个人的理解是这样的。

      以SUID权限为例,SUID这种权限设立的目的是什么,就是提供一种可控的超级权限。比如passwd命令,运行passwd这个程序后,进程的有效用户ID是root,理论上可以为所欲为(即对shadow这个文件想怎么改就怎么改),但是passwd程序的代码已经被写死了,用户所做的操作都要经过passwd这个程序的检验,符合标准了才能够进行,否则程序就会提示出错!(也就是说不能像vim那样随意对shadow文件进行更改,只能在某种规范下进行更改)。

      同时,这种权限应该是有限制的,不能够进行任意传播。比如,像man这种能够执行shell命令的程序,它执行shell命令就是通过fork-exec机制来执行了,在某些distribution中有一个man用户,man程序属于这个用户,并且设置了SUID位。也就是说我任意一个普通用户运行man程序后的有效用户都是man,如果此时这个普通用户在man程序中执行shell命令的话,这个shell命令进程的有效用户应不应该是man呢,设置用户ID应不应该继续保留呢,当然不行啦,这样的话一个普通用户不就能够通过这种方式来具有了man用户的权限了!(如果保留了设置用户ID,那么在子进程中可以调用setuid函数来使进程的有效用户ID变成设置用户ID,同样能达到上面所说的目的)

      OK,上面说的这么一大串,就是为啥SUID或者SGID程序不应该调用system函数来执行一个shell命令,因为这样会传播进程的设置用户ID和有效用户ID,将它传递给子进程,这样就会产生bug了。理论上是这样的,但是实际中我发现貌似不行,我的centos6.6上就不能模拟出这个bug来,比如我有这么一段程序:

      getresuid程序的代码如下:

     1 /*  这个程序的作用是获取进程的三个用户ID,它的可执行文件被做了一个软链接到/home/michael/bin下面
     2  * */
     3 #include<errno.h>
     4 #include<string.h>
     5 #include<stdlib.h>
     6 #include<stdarg.h>
     7 #include<stdio.h>
     8 #include<sys/types.h>
     9 #include<unistd.h>
    10 #define BUFSIZE 512
    11 void err_exit(char *fmt,...);
    12 int main(int argc,char *argv[])
    13 {
    14     uid_t ruid,euid,suid;
    15 
    16     if(-1 == getresuid(&ruid,&euid,&suid))
    17     err_exit("[getresuid]: ");
    18     printf("real:%d	effective:%d	set-user:%d
    ",ruid,euid,suid);
    19 
    20     return 0;
    21 }

      system程序的代码如下:

     1 #include<stdlib.h>
     2 #include<stdio.h>
     3 int main(int argc,char *argv[])
     4 {
     5     uid_t ruid,euid,suid;
     6 
     7     if(-1 == getresuid(&ruid,&euid,&suid))
     8     err_exit("[getresuid]: ");
     9     printf("real:%d	effective:%d	set-user:%d
    ",ruid,euid,suid);
    10 
    11     system("getresuid");
    12     return 0;
    13 }

      这个system程序的功能就是首先输出进程的real uid,effective uid,和set-user id,然后再来通过system函数调用getresuid程序再来把这三个uid输出一遍,我把system这个可执行文件变成root所有,并加上suid权限,执行的结果如下图:

      

      第一个uid的输出结果是符合预期的,即由于SUID权限位的设置,有效用户ID是0。但是后面第二个uid的输出却和想象的有点不同,理论上,system函数应该是能够传递set-user ID和有效用户ID给子进程的,但是这里三个UID全部都变成了500,没一个是root,这个可能是因为system函数在exec之前将所有三个UID都变成了实际用户ID,或者是sh命令在exec之前将所有三个UID都变成了实际用户ID。

      照这么看来,貌似在SUID程序中调用system函数也未尝不可,但是为了保险起见,还是自己通过fork和exec动手来实现一个system吧,然后在exec之前把三个UID都设置成实际用户ID。

  • 相关阅读:
    D. Time to Run【构造】
    P3388 割顶 【求割点个数】
    处女座的测验 素数,构造
    处女座与复读机 DP
    求一个分数小数点后指定位数的数字
    安卓开发创建活动,布局,添加按钮,she使用Toast,设菜单,使菜单相关联等操作
    三进制 处女座的砝码 高精度
    上海高校程序设计联赛 D-CSL的字符串 栈模拟
    区间DP经典 石子合并
    区间DP 洛谷P2858牛奶零食
  • 原文地址:https://www.cnblogs.com/bwangel23/p/4239889.html
Copyright © 2011-2022 走看看