system 函数

system 函数可用来方便地执行一个命令字符串。

#include <stdlib.h>
int system(const char *cmdstring); /* 返回值:(见下)*/

如果 cmdstring 是一个空指针,则仅当命令处理程序可用时,system 返回非 0 值。可利用这一特征来确定某个系统是否支持 system 函数,UNIX 系统上 system 总是可用的。
因为 system 在其实现中调用了 fork、exec 和 waitpid,因此有 3 种返回值。
1、fork 失败或者 waitpid 返回除 EINTR 之外的出错,则 system 返回 -1,并设置 errno 以指示错误类型。
2、如果 exec 失败(表示不能执行 shell),则其返回值如同 shell 执行了 exit(127)。
3、3 个函数都成功,则 system 的返回值是 shell 的终止状态,此时取决于 waitpid。
下面是 system 函数的 POSIX.1 实现,并使用了[url=http://aisxyz.iteye.com/admin/blogs/2391519]wait 和 waitpid 函数[/url]中编写的 pr_exit 函数对其进行测试。注意,为了完整性这里添加了信号处理部分,可参考本人后面与信号相关的博文笔记来了解具体的信号函数的用法。

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>

extern void pr_exit(int status);

/* with appropriate signal handling */
int mySystem(const char *cmdstring){
pid_t pid;
int status;
struct sigaction ignore, saveintr, savequit;
sigset_t chldmask, savemask;

if(cmdstring == NULL)
return 1; // always a command processor with UNIX

ignore.sa_handler = SIG_IGN; // ignore SIGINT and SIGQUIT
sigemptyset(&ignore.sa_mask);
ignore.sa_flags = 0;
if(sigaction(SIGINT, &ignore, &saveintr) < 0)
return -1;
if(sigaction(SIGQUIT, &ignore, &savequit) < 0)
return -1;
sigemptyset(&chldmask); // now block SIGCHLD
sigaddset(&chldmask, SIGCHLD);
if(sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0)
return -1;

if((pid=fork()) < 0){
status = -1; // probably out of processes
}else if(pid == 0){
/* restore previous signal action & reset signal mask */
sigaction(SIGINT, &saveintr, NULL);
sigaction(SIGQUIT, &savequit, NULL);

execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); // execl error
}else{
while(waitpid(pid, &status, 0) < 0){
if(errno != EINTR){
status = -1; // error other than EINTR from waitpid
break;
}
}
}
/* restore previous signal action & reset signal mask */
if(sigaction(SIGINT, &saveintr, NULL) < 0)
return -1;
if(sigaction(SIGQUIT, &savequit, NULL) < 0)
return -1;
if(sigprocmask(SIG_SETMASK, &savemask, NULL) < 0)
return -1;

return status;
}

int main(void){
int status;

if((status=mySystem("date")) < 0)
printf("mySystem 1 error\n");
pr_exit(status);

if((status=mySystem("nosuchcommand")) < 0)
printf("mySystem 2 error\n");
pr_exit(status);

if((status=mySystem("who; exit 44")) < 0)
printf("mySystem 3 error\n");
pr_exit(status);

exit(0);
}

注意,这里调用 _exit 而不是 exit 主要是为了防止任一标准 I/O 缓冲(这些缓冲会在 fork 中由父进程复制到子进程)在子进程中被冲洗。
执行结果:

$ ./mySystem.out
2017年 09月 11日 星期一 23:25:21 CST
normal terminaltion, exit status = 0 # 对于 date
sh: nosuchcommand: command not found
normal terminaltion, exit status = 127 # 对于无此种命令
lei tty1 2017-04-27 04:07 (:0)
lei pts/1 2017-07-12 23:10 (:0.0)
lei pts/2 2017-08-19 08:02 (:0.0)
normal terminaltion, exit status = 44 # 对于 exit

在一个设置用户 ID 程序中调用 system 将是一个安全性方面的漏洞。下面这个 tsys 程序说明了这一点。

#include <stdio.h>
#include <stdlib.h>

extern void pr_exit(int status);

int main(int argc, char *argv[]){
int status;
if((status=system(argv[1])) < 0)
printf("system error\n");
pr_exit(status);
exit(0);
}

接下来是另一个程序 printuids,它打印实际用户 ID 和有效用户 ID。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void){
printf("real uid = %d, effective uid = %d\n", getuid(), geteuid());
exit(0);
}

运行 tsys 程序,得到:

$ ./tsys.out ./printuids.out
real uid = 500, effective uid = 500 # 正常执行,无特权
normal terminaltion, exit status = 0
$
$ su # 成为超级用户
密码:
# chown root tsys.out # 更改所有者
# chmod u+s tsys.out # 增加设置用户 ID
# ls -l tsys.out # 检验文件权限和所有者
-rwsr-xr-x. 1 root root 7412 9月 12 00:07 tsys.out
#
# exit # 退出超级用户 shell
$
$ ./tsys.out ./printuids.out
real uid = 500, effective uid = 0 # 权限提升了(跟系统实现有关)!
normal terminaltion, exit status = 0

由此可见,给予 tsys 程序的超级用户权限在 system 中执行了 fork 和 exec 之后仍被保持了下来(注意,有些实现通过更改 /bin/sh,当有效用户 ID 与实际用户 ID 不匹配时,会将有效用户 ID 设置为实际用户 ID,这样就避免了该安全漏洞)。
如果一个进程正以特殊的权限(设置用户 ID 或设置组 ID)运行,它又想生成另一个进程执行另一个程序,则它应当直接使用 fork 和 exec,而且在 fork 之后、exec 之前要更改回普通权限。设置用户 ID 或设置组 ID 程序决不应调用 system 函数(这种警告的一个理由是:system 调用 shell 对命令字符串进行语法分析,而 shell 使用 IFS 变量作为其输入字段分隔符。早期的 shell 版本在被调用时不将此变量重置为普通字符集。这就允许一个恶意的用户在调用 system 前设置 IFS,造成 system 执行一个不同的程序)。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值