2010-03-20 89 views
29

我正在編寫一些以非特權用戶身份運行的軟件(在C++中,用於Linux/Mac OSX),但在某些時刻需要root權限(以創建新的虛擬設備)。如何以編程方式獲得root權限?

以root身份運行此程序不是一個選項(主要用於安全問題),我需要知道「真實」用戶的身份(uid)。

有沒有辦法模仿「sudo」命令行爲(請求用戶密碼)暫時獲得root權限並執行特定任務?如果是這樣,我會使用哪些功能?

非常感謝您的幫助!

回答

12

原來的答覆

你可能會考慮在可執行文件本身設置了setuid開關。維基百科上有一個article,它甚至可以非常有效地向您顯示geteuid()getuid()之間的區別,前者用於找出您正在「模擬」的用戶,後者用於「您」的用戶。 sudo進程,例如,geteuid應該返回0(root)並獲取用戶的id,但是,其子進程確實以root身份運行(可以使用sudo id -u -r進行驗證)。

我不認爲有一種方法可以輕鬆地以編程方式獲得root訪問權限 - 畢竟,應用最小權限原則,您爲什麼需要?通常的做法是隻用有限的權限運行有限的代碼部分。許多守護進程等也在現代系統下建立,以他們自己的用戶身份運行,並擁有他們所需的大部分特權。只有非常特定的操作(安裝等)才需要root權限。

2013更新

我原來的答覆代表(雖然我的2013自可能會使它的一個更好的工作比我2010一),但如果你正在設計一個需要root訪問權限的應用程序,你可能想考慮需要什麼樣的根訪問權限,並考慮使用POSIX Capabilities(man page)。這些與L4等人實施的capability-based security不同。 POSIX功能允許您的應用程序被授予root權限的子集。例如CAP_SYS_MODULE將允許你插入內核模塊,但是不會給你其他的根權力。這用於分發例如Fedora has a feature to completely remove setuid binaries不分青紅皁白地訪問。

這很重要,因爲作爲一名程序員,您的代碼顯然是完美的!但是,你所依賴的圖書館(嘆息,如果你只是寫了它們!)可能存在漏洞。使用功能,您可以限制使用此漏洞利用,並使自己和公司免受安全相關審查。這讓每個人都更開心。

+0

是的,我的方法不對。設置粘性位是實現這一目標的好方法。非常感謝你。 – ereOn 2010-03-20 16:41:40

+0

工程就像一個魅力!謝謝;) – ereOn 2010-03-20 16:42:30

+2

當你這樣做時,你必須非常小心安全性,因爲你的應用程序現在成爲特權升級的目標 – 2010-03-21 20:50:32

0

您可以嘗試啓動命令以通過後臺shell創建虛擬設備(包括sudo)。當sudo詢問它時,請在您自己的對話框中輸入用戶密碼並將其輸入到shell中。還有其他解決方案,如使用gksu,但不保證在每臺機器上均可用。

您不以root身份運行整個程序,但只需要root的一小部分。你應該爲此產生一個單獨的進程,sudo可能對你有幫助。

+0

謝謝您的回答。不幸的是,該程序並不總是需要root權限。有時你可能只是想以某種方式使用它,所以它不需要執行特定的任務。在這種情況下,不需要詢問密碼。 我發現了討論setuid()的線程。我要調查這一點。 – ereOn 2010-03-20 16:20:56

+2

sudo保證在每臺機器上都可用?我看過沒有它的安裝。 – ziggystar 2010-03-20 16:21:00

+0

不,它在默認情況下沒有在fedora上配置。我還親自阻止不在「wheel」組中的用戶使用sudo或su作爲傳統unix行爲的擴展。 – 2010-03-20 16:26:47

19

如果您每次都需要root權限,最好的方法是以root用戶身份啓動程序,並使用setuidsetgid將其刪除(在子進程中)。這時候,就需要綁定到受限制的端口80

如果獲得根權限是例外而非規則和程序交互運行的Apache做什麼,另一種方式是寫一個程序add_interface和執行

sudo add_interface args 

並讓sudo爲您處理驗證。您可能想要使用gksu,gksudo,kdesu或kdesudo等圖形前端,而不是sudo。我不會嘗試自己實現安全的密碼輸入;它可能是一個棘手的問題,你可能會留下空洞的安全漏洞和功能問題(你是否支持指紋識別器?)。

另一種替代方法是polkit,以前稱爲PolicyKit。

+0

Aaaah,我剛剛發佈了我的答案,並提出了 - 這是很好的建議。 +1。 – 2010-03-20 16:25:53

+0

PolicyKit爲+1。 – 2010-06-30 09:27:29

7

你不能獲得root權限,你必須從他們開始,並根據需要減少你的權限。這樣做的通常方法是安裝帶有setuid位的程序:這將使用文件所有者的有效用戶標識運行程序。如果您在sudo運行ls -l,你會看到,它是安裝方式:

-rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo 

當你的程序是用root權限運行,你可以調用setuid(2)系統調用你的有效用戶標識更改爲某個非特權用戶。我相信(但還沒有嘗試過),你可以以root身份安裝你的程序,setuid位開啓,立即減少權限,然後根據需要恢復權限(但是,有可能一旦你降低你的權限,你將不會能夠恢復它)。

更好的解決方案是打開需要以root用戶身份運行的程序,並在打開setuid位的情況下安裝它。當然,您需要採取合理的預防措施,使其不能在您的主程序之外調用。

+0

謝謝!事實上,這似乎是要走的路。我要去嘗試並給出結果;) – ereOn 2010-03-20 16:29:43

2

你可能想看看這些API:

setuid, seteuid, setgid, setegid, ... 

他們在頭<unistd.h>在Linux系統中定義的(不知道很多關於MAC,但你應該有一個類似頭有太)。

我能看到的一個問題是,該進程必須具有足夠的權限來更改其用戶/組ID。否則,對以上功能的調用將導致出錯,errorno設置爲EPERM

我建議您以root用戶的身份運行程序,並在最開始時將有效的用戶標識(使用seteuid)更改爲不受尊敬的用戶。然後,無論何時您需要提升權限,請提示輸入密碼,然後再次使用seteuid以恢復爲root用戶。

4

通常這是通過使你的二元suid根。

管理這一點,以便對您的程序的攻擊是很難的一個方法是儘量減少運行像這樣根代碼:

int privileged_server(int argc, char **argv); 
int unprivileged_client(int argc, char **argv, int comlink); 


int main(int argc, char **argv) { 
    int sockets[2]; 
    pid_t child; 
    socketpair(AF_INET, SOCK_STREAM, 0); /* or is it AF_UNIX? */ 

    child = fork(); 
    if (child < 0) { 
     perror("fork"); 
     exit(3); 
    } elseif (child == 0) { 
     close(sockets[0]); 
     dup2(sockets[1], 0); 
     close(sockets[1]); 
     dup2(0, 1); 
     dup2(0, 2); /* or not */ 
     _exit(privileged_server(argc, argv)); 
    } else { 
     close(sockets[1]); 
     int rtn; 
     setuid(getuid()); 
     rtn = unprivileged_client(argc, argv, sockets[0]); 
     wait(child); 
     return rtn; 
    } 
} 

現在通過FD通信鏈路的非特權碼會談的特權代碼(這是一個連接的插座)。相應的特權代碼使用stdin/stdout作爲其鏈接的結尾。

特權代碼需要驗證其需要執行的每個操作的安全性,但由於此代碼與非特權代碼相比較小,因此應該相當容易。

+0

你的第二塊我認爲你的意思是'孩子> 0' – 2010-06-30 09:20:26

+0

其實我相信它應該是'else if(child == 0)' - 'else'塊'等待孩子,所以孩子需要成爲中間的'else if'塊。 – user470379 2010-12-10 20:07:58

+0

user470379是正確的。固定。 – Joshua 2010-12-10 20:44:34

2

在OS X上,您可以使用AuthorizationExecuteWithPrivileges函數。 Authorization Services Tasks上的頁面詳細討論了這個(和相關的)功能。

這裏有一點C++代碼具有管理員權限執行的程序:

static bool execute(const std::string &program, const std::vector<std::string> &arguments) 
{ 
    AuthorizationRef ref; 
    if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &ref) != errAuthorizationSuccess) { 
     return false; 
    } 

    AuthorizationItem item = { 
     kAuthorizationRightExecute, 0, 0, 0 
    }; 
    AuthorizationRights rights = { 1, &item }; 
    const AuthorizationFlags flags = kAuthorizationFlagDefaults 
            | kAuthorizationFlagInteractionAllowed 
            | kAuthorizationFlagPreAuthorize 
            | kAuthorizationFlagExtendRights; 

    if (AuthorizationCopyRights(ref, &rights, kAuthorizationEmptyEnvironment, flags, 0) != errAuthorizationSuccess) { 
     AuthorizationFree(ref, kAuthorizationFlagDestroyRights); 
     return false; 
    } 

    std::vector<char*> args; 
    for (std::vector<std::string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) { 
     args.push_back(it->c_str()); 
    } 
    args.push_back(0); 

    OSStatus status = AuthorizationExecuteWithPrivileges(ref, program.c_str(), kAuthorizationFlagDefaults, &args[0], 0); 

    AuthorizationFree(ref, kAuthorizationFlagDestroyRights); 
    return status == errAuthorizationSuccess; 
} 
+0

我們在哪裏調用這個函數?什麼是參數參數?我試圖在main函數的開頭調用,但它崩潰了。參數爲null。我的問題是在/ var/log創建一個文件。但是許可是必要的。我該如何處理這個問題? – gkhanacer 2017-11-03 14:47:53

+0

值得一提的是這些代碼需要的所有庫,不是嗎? – Konstantin 2017-11-16 07:58:58

相關問題