需求分析:

这里先要说明的是,这一篇不是QT系列的文章,而是关于Web的,之所以要写这篇,是因为以前做Web相关开发的时候,经常涉及到与linux底层命令打交道,比如说创建一个目录,删除一个目录,或者是执行一个自定义的脚本。关于PHP如何调用、执行Linux的底层命令,以前也研究过,基本上实现了自己需要的功能,但是有些地方一直没有弄明白。今天又偶然碰到了,趁着这个机会向大家描述一下一步一步应该如何实现,并最后附上相关C代码。

原理实现:

首先,一般搭建的Web站点都是采用Apache或Nginx,因此当使用php执行linux的命令的时候,在linux端体现出的时Apache或Nginx用户的身份来执行的。通常情况下基于安全考虑,Apache或Nginx在linux端的默认用户是不具有很高的权限的,比如删除、创建等,因此我们必须通过一种方式,为其赋予一定的权限。在我以前写过的文章中,我采用了一种方法,即将Apache或Nginx的默认用户修改了,给那个用户赋予了很高的权限,虽然达到了我的目的,但是恰恰造成了最大的隐患,即:web服务器默认用户权限设置过大,很容易受到来自外界的攻击,甚至不用外界,我自己在php端执行一个命令能将我整个站点删除掉,而在linux端对此基本没有任何防御能力。基于此,我们提出一种方式,在linux端不修改web服务器默认用户的权限,而是当执行命令时由我们来自己控制这条命令应当具有何种用户的执行权限,或者root用户,或者普通用户完全由我们传递的参数决定。

其次,基于上面的阐述,基本实现思路为:接受php传递来的username和passwd的参数,在linux端新建一个进程,然后将该进程模拟为一个用户的身份,即设置该进程的用户实际、有效的用户ID和用户组ID,然后再执行某条命令,此时执行该命令的过程就好像username用户本身执行那条命令一样。username能执行的有效命令只能在自己的职权范围之内,比如:如果username是普通用户,则它无法通过执行命令删除其他用户或root用户的文件。这样,在一定程度上实现了安全的可控性。

最后,根据上面提出的改变某个进程的实际、有效用户ID和用户组ID需要用到setuid()和setgid(),而关于这两个函数这里还有需要注意的地方,如果执行它的是特权用户,即root,则它可以任意设置自己的uid和gid,即可以模拟任意用户;而如果执行它的时普通用户,则它只能设置自己的uid和gid,而不能设置为其它用户的,即无法模拟其它用户。为了解决这个问题就要用到linux的一些特性,即设置文件的用户标记位:s,使文件在执行时具有文件所有者的权限。这样,Apache或Nginx默认用户执行这个文件的时候就类似于该文件的所有者在执行它,而我们利用root用户创建该文件,就相当于root用户在执行该文件,此时setuid和setgid就可以将进程模拟为任何用户的身份了。

实现代码:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/wait.h>
  7. #include <pwd.h>
  8. #include <math.h>
  9. #include <time.h>
  10. //Usage: exefile   command    work_directory   username    password
  11. int main(int argc, char *argv[])
  12. {
  13. char *username = NULL;
  14. char *password = NULL;
  15. char *command  = NULL;
  16. char *workdir = NULL;
  17. struct passwd *stpasswd =   NULL;
  18. if(argc == 5)
  19. {
  20. command   = argv[1];
  21. workdir   = argv[2];  //work directory
  22. username  = argv[3];
  23. password  = argv[4];
  24. }
  25. else
  26. {
  27. return 1;
  28. }
  29. //printf("username = %s\n",username);
  30. //printf("password = %s\n",password);
  31. //printf("workdir = %s\n",workdir);
  32. //printf("command = %s\n",command);
  33. int result = 0;//auth(username,password);     //Ensure the user is a legel user in the system
  34. if(result == 0)  //auth successfully
  35. {
  36. int kidstatus, deadpid;
  37. //! fork()克隆出一个完全相同的进程出来;它会复制当前进程的所有变量,就好像一个进程克隆出来另一个一样
  38. //! fork()函数的返回值有三种情况:=0代表克隆出来的那个进程  >0代表“父”进程   <0 执行出错
  39. //! 这种情况一般就是fork出来新的进程,【可选:进行身份切换】,然后利用它来执行execlp(),执行相关的命令
  40. pid_t kidpid = fork();
  41. if (kidpid == -1)
  42. {
  43. printf("fork error");
  44. return 1;
  45. }
  46. if (kidpid == 0)
  47. {
  48. //! getpwnam():获取用户登录相关的信息
  49. //! 头文件:#include <pwd.h> #include <sys/types.h>
  50. stpasswd = getpwnam(username);
  51. //! setgid():设置实际用户组ID和有效用户组ID
  52. //! setuid():设置实际用户ID和有效用户ID
  53. //! chdir() : 修改当前目录
  54. //! 注意:如果是一个非特权用户,则它执行setgid和setuid只能设置为自己的gid和uid,而不能设置其它任何数值
  55. //! 如果是一个特权用户(即:拥有root权限),则可以利用setgid和setuid设置为任意数值,这也就是为什么最终
  56. //! 编译出来的文件要通过:chmod u+s 设置特权位
  57. setgid( (int)(stpasswd->pw_gid));   //Set current usergroup
  58. setuid( (int)(stpasswd->pw_uid));   //Set current user
  59. chdir(workdir);                                         //change work directory
  60. //! int execlp(const char *file, const char *arg, ..., (char *)0);
  61. //! execlp()会从PATH环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后
  62. //! 将第二个参数以后的参数当作该文件的argv[0], argv[1] ...,最后一个参数必须用空指针(NULL)结束
  63. //! 下面的命令就是:/bin/bash -c command执行
  64. int rv = execlp("/bin/bash", "/bin/bash", "-c", command, NULL);
  65. fflush(stdout);
  66. return rv;
  67. }
  68. //! we only get here if we're the parent process.
  69. //! waitpid()阻塞等待子进程结束
  70. //! 函数原型:
  71. deadpid = waitpid(kidpid, &kidstatus, 0);
  72. if (deadpid == -1)
  73. {
  74. printf("error to fork a process!");
  75. return 1;
  76. }
  77. else
  78. {
  79. return 0;
  80. }
  81. }
  82. else
  83. {
  84. printf("Authenticate failed\n");
  85. return 1;
  86. }
  87. }

最后,利用gcc编译该文件:gcc  test_exec.c  -o  test_exec;编译完成后为它赋予权限:chmod 777 test_exec;然后设置用户标记位:s, chmod  u+s  test_exec

执行测试:

1. 利用root用户创建一个del_test文件,然后切换到普通用户,测试能否删除;然后用我们的命令模拟root用户看能否删除

2. 利用root用户创建一个del_test的文件,切换到zhangsan用户,利用命令模拟lisi用户,看能够删除

通过以上,我们可以看出,利用该命令我们可以模拟任何用户的身份,相当于任何权限都是由你自己控制,在一定程度上保证了安全性。

总结:

对似懂非懂的知识点一定要明白其原理,不然始终不知道应该如何来做。加油!这段时间的网盘开发没有涉及太多挑战性的东西,期待UDT的研究。

参考:http://blog.csdn.net/houqd2012/article/details/34165381

万能脚本助Web执行底层Linux命令的更多相关文章

  1. 【夸QT十一】外来物品:通用脚本帮助Web运行基础Linux命令

    需求分析: 需要注意的是在这里第一次,这个人是不是QT系列文章,它是关于Web的,之所以写这篇文章.这是因为碍着Web相关开发时间,而且往往涉及linux与底层指令处理.例如,创建一个文件夹,删除一个 ...

  2. 后台执行UNIX/Linux命令和脚本的五种方法

    hiveserver 后台启动 nohup "${HIVE_HOME}"/bin/hive --service hiveserver2 & 1. 使用&符号在后台执 ...

  3. Linux centosVMware shell脚本介绍、shell脚本结构和执行、date命令用法、shell脚本中的变量

    一. shell脚本介绍 shell是一种脚本语言 aming_linux blog.lishiming.net 可以使用逻辑判断.循环等语法 可以自定义函数 shell是系统命令的集合 shell脚 ...

  4. python模块之 - subprocess执行unix/linux命令

    subprocess模块提供了一种一致的方法来创建和处理附加进程,与标准库中的其它模块相比,提供了一个更高级的接口,subprocess模块用来生成子进程,并可以通过管道连接它们的输入/输出/错误,以 ...

  5. 批量执行(Linux命令,上传/下载文件)

    前言: 每个公司的网络环境大都划分 办公网络.线上网络,之所以划分的主要原因是为了保证线上操作安全: 对于外部用户而言也只能访问线上网络的特定开放端口,那么是什么控制了用户访问线上网络的呢? 防火墙过 ...

  6. shell脚本介绍、shell脚本结构和执行、date命令用法、shell脚本中的变量

    7月11日任务 20.1 shell脚本介绍20.2 shell脚本结构和执行20.3 date命令用法20.4 shell脚本中的变量 20.1 shell脚本介绍 1.shell脚本语言是linu ...

  7. 吻逗死(windows)系统下自动部署脚本(for java spring*)及linux命令行工具

    转载请注明出处:https://www.cnblogs.com/funnyzpc/p/10051647.html (^^)(^^)自動部署腳本原本在上個公司就在使用,由於近期同事需要手動部署一個Spr ...

  8. jenkins 执行远程linux命令

    在Jenkins中进行构建时,可能需要首先SSH登录到一个远程服务器以执行必要的脚本,然后再执行构建.这时,需要安装SSH Plugin,并进行如下配置.1.在Jenkins界面,系统管理->管 ...

  9. 一个shell脚本,让你的linux命令行酷炫起来

    可调用如下函数达到echo出来带颜色的文字.._echo_error() { echo -ne "\033[31;1m $1\033[0m\n";}_echo_ok() { ech ...

随机推荐

  1. .bak文件在英文版的sqlserver2014如何生成和恢复

    生成bak备份文件 1.选择数据库 2.右击选择task 3.选择backup 4.

  2. ios registerNib: and registerClass:

    先看看apple官网简述: registerNib:forCellWithReuseIdentifier: Register a nib file for use in creating new co ...

  3. 第七篇、CSS3新增属性

    <!-- 1.透明度 opacity(设置不透明度):0.2: --rgba --background-color:rgba(255,0,0,0.8); 2.块阴影和圆角阴影 --box-sha ...

  4. 【html】【13】特效篇--下拉导航

    html代码: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" ...

  5. Java实战之04JavaWeb-08文件上传与下载

    一.文件上传 1.文件上传的实质是什么 文件的拷贝,文件从客户端拷贝服务器端 2.文件上传的工作 (1)客户端怎样将本地文件输出去? 1)文件上传的表单项 <input type=”file”& ...

  6. const char*、char*、char* const、char[]、string的区别

    1.const char* p: p is a pointer to const char(char const* p 一样)   意思就是不能通过p指针来修改p指向的内容(但是内容可以修改). 2. ...

  7. node.js里的forEach()也是异步的吗?

    博客已经迁移到www.imyzf.com,本站不再更新,请谅解! node里几乎所有用到回调函数的地方,都是异步的,回调函数后面的代码很可能比回调函数中的代码后先执行,特别是数据库操作.当然,node ...

  8. IE 6/7下自赋值导致 overflow 溢出

    情景是要限制一个textarea的最大输入字数(100字,  这字数限制也太少了点吧,不大气) 由于限制输入后需要允许 回退,全选等功能键,故放弃keyup, keydown组合 选用property ...

  9. [转]内嵌WORD/OFFICE的WINFORM程序——DSOFRAMER使用小结

    最近一直想用VC#2005做个内嵌WORD/OFFICE的WINFORM程序,目前主要有以下解决途径: 1.直接通过API把WORD/OFFICE的窗口句柄给放到WINFORM中(感觉较为复杂): 2 ...

  10. 几条特殊的SQL语句

    1, 有case情况. select trunc(exf_payment_receipt.work_date),exf_payment_receipt.exchange_code,exf_paymen ...