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

需求分析:

        需要注意的是在这里第一次,这个人是不是QT系列文章,它是关于Web的,之所以写这篇文章。这是因为碍着Web相关开发时间,而且往往涉及linux与底层指令处理。例如,创建一个文件夹,删除一个文件夹,或者是运行一个自己定义的脚本。关于PHP怎样调用、运行Linux的底层命令。曾经也研究过,基本上实现了自己须要的功能,可是有些地方一直没有弄明确。今天又偶然碰到了,趁着这个机会向大家描写叙述一下一步一步应该怎样实现,并最后附上相关C代码。

原理实现:

        首先,一般搭建的Web网站都是採用Apache或Nginx。因此当使用php运行linux的命令的时候。在linux端体现出的时Apache或Nginx用户的身份来运行的。通常情况下基于安全考虑。Apache或Nginx在linux端的默认用户是不具有非常高的权限的,比方删除、创建等。因此我们必须通过一种方式。为其赋予一定的权限。

在我曾经写过的文章中,我採用了一种方法,即将Apache或Nginx的默认用户改动了,给那个用户赋予了非常高的权限。尽管达到了我的目的,可是恰恰造成了最大的隐患,即:webserver默认用户权限设置过大,非常easy受到来自外界的攻击,甚至不用外界。我自己在php端运行一个命令能将我整个网站删除掉。而在linux端对此基本没有不论什么防御能力。基于此。我们提出一种方式。在linux端不改动webserver默认用户的权限,而是当运行命令时由我们来自己控制这条命令应当具有何种用户的运行权限。或者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就能够将进程模拟为不论什么用户的身份了。

实现代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>
#include <math.h>
#include <time.h>


//Usage: exefile   command    work_directory   username    password
int main(int argc, char *argv[])
{
	char *username = NULL;
	char *password = NULL;
	char *command  = NULL;
	char *workdir = NULL;
	struct passwd *stpasswd =	NULL;
		
	if(argc == 5) 
	{
		command   = argv[1];
		workdir	  = argv[2];  //work directory
		username  = argv[3];
		password  = argv[4];		
	}
	else
	{
		return 1;
	}

	//printf("username = %s\n",username);
	//printf("password = %s\n",password);
	//printf("workdir = %s\n",workdir);
	//printf("command = %s\n",command);

	
	int result = 0;//auth(username,password);	  //Ensure the user is a legel user in the system
	
	if(result == 0)  //auth successfully
	{	
		
		int kidstatus, deadpid;

		//! fork()克隆出一个全然同样的进程出来;它会复制当前进程的全部变量,就好像一个进程克隆出来还有一个一样
		//! fork()函数的返回值有三种情况:=0代表克隆出来的那个进程  >0代表“父”进程   <0 运行出错
		//! 这样的情况一般就是fork出来新的进程。【可选:进行身份切换】,然后利用它来运行execlp(),运行相关的命令
 		pid_t kidpid = fork();

		if (kidpid == -1) 
		{
			printf("fork error");
			return 1;
		}

		if (kidpid == 0) 
		{		
			//! getpwnam():获取用户登录相关的信息
			//! 头文件:#include <pwd.h> #include <sys/types.h>
			stpasswd = getpwnam(username);                       

			//! setgid():设置实际用户组ID和有效用户组ID
			//! setuid():设置实际用户ID和有效用户ID
			//! chdir() : 改动当前文件夹
			//! 注意:假设是一个非特权用户,则它运行setgid和setuid仅仅能设置为自己的gid和uid。而不能设置其他不论什么数值
			//! 假设是一个特权用户(即:拥有root权限)。则能够利用setgid和setuid设置为随意数值,这也就是为什么终于
			//! 编译出来的文件要通过:chmod u+s 设置特权位
			setgid( (int)(stpasswd->pw_gid));   //Set current usergroup
			setuid( (int)(stpasswd->pw_uid));   //Set current user
			chdir(workdir); 										//change work directory

			//! int execlp(const char *file, const char *arg, ..., (char *)0);
			//! execlp()会从PATH环境变量所指的文件夹中查找符合參数file的文件名称,找到后便运行该文件,然后
			//! 将第二个參数以后的參数当作该文件的argv[0], argv[1] ...,最后一个參数必须用空指针(NULL)结束
			//! 以下的命令就是:/bin/bash -c command运行
			int rv = execlp("/bin/bash", "/bin/bash", "-c", command, NULL); 
			
			fflush(stdout);			
			
			return rv;	
		}

    //! we only get here if we're the parent process.
    //! waitpid()堵塞等待子进程结束
	//! 函数原型:
    deadpid = waitpid(kidpid, &kidstatus, 0);

    if (deadpid == -1) 
		{
			  printf("error to fork a process!");
			  return 1;
		}
		else
		{
		    return 0;
  	    }
	}
	else
  	{
  		 printf("Authenticate failed\n");
		  return 1;  
	  }
 }

最后。利用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科研。


版权声明:本文博主原创文章,博客,未经同意不得转载。