2012-10-23 44 views
4

Linux是否有一些類似於setuid的C接口,它允許程序使用例如切換到其他用戶的方式切換到不同的用戶。用戶名/密碼? setuid的問題在於它只能由超級用戶使用。setuid等效於非root用戶

我正在運行一個簡單的web服務,它要求作爲登錄用戶執行作業。所以主進程以root用戶身份運行,並且在用戶登錄後fork並調用setuid切換到相應的uid。但是,我對以root身份運行的主要proc不太滿意。我寧願讓它作爲另一個用戶運行,並且有一些機制可以切換到另一個用戶,類似於su(但不啓動新進程)。

+0

您可以將'root'切換爲非特權用戶,然後在需要時切換回(通過[已保存的用戶標識](http://en.wikipedia.org/wiki/User_identifier#Saved_user_ID)機制)切換到不同的用戶。 –

+0

嗯,我不認爲這會增加我的情況下的任何安全性,假設切換回機制不受保護。所以如果主要服務被利用,直到root纔會是一個微不足道的額外步驟。 – Jeroen

+0

這取決於你「被剝削」的含義。如果你的意思是他們可以讓它訪問他們想要的任何代碼,那麼我認爲這是最基本的。他們可以添加代碼來記錄它用來切換用戶的憑證,然後使用這些憑證來利用系統。除非你的意思是根特別會受到影響。 –

回答

4

不,沒有辦法僅使用用戶名和密碼更改UID。 (內核無法以任何方式識別「密碼」的概念 - 它只存在於用戶空間中。)要從一個非root UID切換到另一個,您必須成爲root作爲中間步驟,通常由exec() - 使用setuid二進制。

您的情況的另一種選擇可能是讓主服務器作爲非特權用戶運行,並使其與以root身份運行的後端進程進行通信。

+0

可以編寫此後端過程來檢查提供的密碼,這將提供所需的整體效果。 – caf

+0

Sorta。您仍然無法以這種方式將正在運行的進程從非root升級到root;最好的辦法是讓後端進程按需啓動進程。 – duskwuff

+0

嗯,是的,後端進程必須以root身份運行,就像你在回答中所說的那樣。 – caf

9

首先,setuid()絕對可以被非超級用戶使用。從技術上講,您在Linux中所需的全部是切換到任何用戶的CAP_SETUID(和/或CAP_SETGIDcapability。其次,setuid()setgid()可以更改真實(執行過程的用戶),有效(所有者setuid/setgid二進制)和保存的身份之間的進程標識。

但是,這些都與您的情況無關。

存在一個相對直接但非常健壯的解決方案:在創建任何線程之前,有一個setuid根助手,由服務守護程序分叉並執行,並使用Unix域套接字對在助手和服務之間進行通信,當要執行用戶二進制文件時,該服務將其憑證和管道端點文件描述符傳遞給幫助程序。該幫助程序將安全地檢查所有內容,如果所有內容都符合要求,它將分叉並執行所需的用戶幫助程序,並將指定的管道端點連接到標準輸入,標準輸出和標準錯誤。

用於啓動輔助,儘可能早地將服務的過程,如下:

  1. 創建Unix域socket pair,用於服務和輔助之間特權的通信。

  2. 叉。

  3. 在孩子中,關閉所有多餘的文件描述符,只保留套接字對的一端。將標準輸入,輸出和錯誤重定向到/dev/null

  4. 在父項中,關閉套接字對的子端。

  5. 在孩子中,執行特權助手二進制。

  6. 父母發送一個簡單的消息,可能是一個沒有任何數據的消息,但是包含其憑據的ancillary message

  7. 幫助程序等待來自該服務的初始消息。 當它收到它時,它會檢查憑證。如果證書不通過,它立即退出。

輔助消息中的證書定義始發過程UIDGID,和PID。儘管進程需要填寫這些內核,但內核證實它們是真實的。當然,該幫助程序驗證UIDGID是否如預期的那樣(對應於該服務應該運行的帳戶),但訣竅是獲取符號鏈接指向的文件統計信息/proc/PID/exe。這是發送證書的過程的真正可執行文件。您應該驗證它與安裝的系統服務守護程序(由root:root擁有,位於系統二進制目錄中)相同。

有一個非常簡單的攻擊可能會破壞安全到目前爲止。惡意用戶可能會創建自己的程序,它會正確分叉並執行助手二進制文件,併發送帶有其真實憑據的初始消息 - 但在助手有機會檢查憑據實際引用的內容之前,用自己的正確系統二進制文件替換它本身!

那攻擊平凡三個進一步的步驟擊敗:

  1. 助手程序產生(密碼安全)的僞隨機數,說1024位,並將其發送回父。

  2. 父母發回數字,但又在輔助消息中添加憑證。

  3. 助手程序驗證UIDGIDPID沒有改變,那/proc/PID/exe仍然指向正確的服務守護程序二進制文件。 (我只是重複全程把關。)

在步驟8,助手已經確定了插座的另一端執行的二進制它應該被執行。發送它必須發送回來的隨機cookie,意味着另一端不能事先用消息「填充」套接字。當然,這假設攻擊者無法事先猜出僞隨機數。如果你想要小心,你可以從/dev/random中讀取一個合適的cookie,但記住它是一個有限的資源(如果內核沒有足夠的隨機性可能會阻塞)。我個人只是讀從/dev/urandom說1024位(128字節),並使用它。

此時,助手已經確定套接字對的另一端是服務守護進程,並且幫助者可以信任控制消息,只要它可以信任服務守護進程即可。 (我假設這是服務守護進程產生用戶進程的唯一機制;否則,你需要在每一個進一步的消息中重新傳遞憑證,並且每次在助手中重新檢查它們。)

每當服務守護程序希望執行用戶二進制,它

  1. 創建必要的管道(一個用於饋送標準輸入到用戶的二進制,一個取回來自用戶的二進制標準輸出)

  2. 將消息發送到包含

    • 身份到作爲運行的二進制輔助;任一用戶(和組)名稱,或UID和GID(S)
    • 路徑提供給二進制
    • 含有文件描述符的用戶的二進制端點的輔助信息的二進制
    • 命令行參數數據管道

每當輔助得到這樣的消息,它叉子。在子代中,它將標準輸入和輸出替換爲輔助消息中的文件描述符,將標識更改爲setresgid()setresuid()和/或initgroups(),將工作目錄更改爲適當的某處並執行用戶二進制文件。父助手進程關閉輔助消息中的文件描述符,並等待下一條消息。

如果輔助程序在沒有更多來自套接字的輸入時退出,那麼它將在服務退出時自動退出。

我可以提供一些示例代碼,如果有足夠的興趣。有很多細節可以正確使用,所以編寫代碼有點乏味。然而,正確書寫,它比例如Apache SuEXEC。

+0

很好的答案。這應該被選爲正確的答案。 – Tagar