需求分析:
這裡先要說明的是,這一篇不是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就可以将程序模拟為任何使用者的身份了。
實作代碼:
#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使用者看能否删除
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0NXYFhGd192UvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LclXRE1UeRd1YxkzRhZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TMzUjNwcTMwATNyYDM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
2. 利用root使用者建立一個del_test的檔案,切換到zhangsan使用者,利用指令模拟lisi使用者,看能夠删除
通過以上,我們可以看出,利用該指令我們可以模拟任何使用者的身份,相當于任何權限都是由你自己控制,在一定程度上保證了安全性。
總結:
對似懂非懂的知識點一定要明白其原理,不然始終不知道應該如何來做。加油!這段時間的網盤開發沒有涉及太多挑戰性的東西,期待UDT的研究。