2010-07-28 177 views
45

我有一個以root身份啓動的守護程序(因此它可以綁定到低端口)。初始化後,我非常想讓它爲了安全起見而放棄root權限。刪除root權限

任何人都可以指向我在已知的正確的在C代碼片段會做到這一點?

我已經閱讀過手冊頁,我已經在不同的應用程序中看到了這個不同的實現,它們都是不同的,有些非常複雜。這是與安全相關的代碼,我真的不希望重蹈其他人的同樣錯誤的重犯。我正在尋找的是一種最佳實踐,即已知的,可移植的便攜式庫函數,我可以使用這些函數以獲得正確的知識。這樣的事情存在嗎?

僅供參考:我以root身份啓動;我需要改變在不同的用戶和gid下運行;我需要讓補充小組正確設置;之後我不需要改回根權限。

+5

這在unixes之間有很大的不同 - 有沒有特別的?如果你需要一個「便攜式」解決方案,它會很麻煩,你最好抓住例如來自OpenSSH的permanent_set_uid()函數 - 在uidswap中。c文件 – nos 2010-07-28 22:05:34

回答

43

爲了刪除所有權限(用戶和組),您需要在用戶之前刪除組。

if (getuid() == 0) { 
    /* process is running as root, drop privileges */ 
    if (setgid(groupid) != 0) 
     fatal("setgid: Unable to drop group privileges: %s", strerror(errno)); 
    if (setuid(userid) != 0) 
     fatal("setuid: Unable to drop user privileges: %S", strerror(errno)); 
} 

如果你是偏執狂:鑑於useridgroupid包含用戶和要刪除該組的ID,並假設有效ID是也根,這是通過調用setuid()setgid()完成,你可以嘗試恢復你的root權限,這應該會失敗。如果沒有失敗,你救助:

if (setuid(0) != -1) 
    fatal("ERROR: Managed to regain root privileges?"); 

另外,如果你還是偏執,你可能要seteuid()setegid()太多,但它不應該是必要的,因爲的setuid()和setgid()已經設置了所有ID,如果該進程屬於root。

補充組列表是一個問題,因爲沒有POSIX函數來設置補充組(有getgroups(),但沒有setgroups())。有一個BSD和Linux擴展setgroups(),你可以使用,它涉及到你。

您還應該chdir("/")或任何其他目錄,以便進程不會保留在根目錄中。

由於你的問題是關於Unix的一般情況,這是非常普遍的方法。請注意,在Linux中,這不再是首選方法。在當前的Linux版本中,您應該將CAP_NET_BIND_SERVICE capability設置爲可執行文件,並以普通用戶身份運行它。不需要root權限。

+1

你也想設置gid - 這可能會因unix的不同而有所不同,因爲setuid/setgid實際上與真實和已保存的id不一致 – nos 2010-07-28 22:06:45

+1

這確實需要一個可移植的解決方案,所以沒有Linux能力fu允許,恐怕。我已經嘗試了setuid()和setgid()的簡單方法;它不會正確設置組(如果您不調用setgroups(),顯然您最終可能仍然是某些根組的成員!)。 – 2010-07-28 22:14:54

+0

@nos謝謝。擴大到覆蓋羣體。如果進程由root擁有(如OP所述),或者它是setuid-root,則setuid()和setgid()已經設置了所有ID(真實,有效和保存)。這是在說明書中。否則,實現將不符合POSIX。 – Juliano 2010-07-28 22:59:59

1

這是我能做些什麼最好:

#define _GNU_SOURCE // for secure_getenv() 


int drop_root_privileges(void) { // returns 0 on success and -1 on failure 
    gid_t gid; 
    uid_t uid; 

    // no need to "drop" the privileges that you don't have in the first place! 
    if (getuid() != 0) { 
     return 0; 
    } 

    // when your program is invoked with sudo, getuid() will return 0 and you 
    // won't be able to drop your privileges 
    if ((uid = getuid()) == 0) { 
     const char *sudo_uid = secure_getenv("SUDO_UID"); 
     if (sudo_uid == NULL) { 
      printf("environment variable `SUDO_UID` not found\n"); 
      return -1; 
     } 
     errno = 0; 
     uid = (uid_t) strtoll(sudo_uid, NULL, 10); 
     if (errno != 0) { 
      perror("under-/over-flow in converting `SUDO_UID` to integer"); 
      return -1; 
     } 
    } 

    // again, in case your program is invoked using sudo 
    if ((gid = getgid()) == 0) { 
     const char *sudo_gid = secure_getenv("SUDO_GID"); 
     if (sudo_gid == NULL) { 
      printf("environment variable `SUDO_GID` not found\n"); 
      return -1; 
     } 
     errno = 0; 
     gid = (gid_t) strtoll(sudo_gid, NULL, 10); 
     if (errno != 0) { 
      perror("under-/over-flow in converting `SUDO_GID` to integer"); 
      return -1; 
     } 
    } 

    if (setgid(gid) != 0) { 
     perror("setgid"); 
     return -1; 
    } 
    if (setuid(uid) != 0) { 
     perror("setgid"); 
     return -1;  
    } 

    // change your directory to somewhere else, just in case if you are in a 
    // root-owned one (e.g. /root) 
    if (chdir("/") != 0) { 
     perror("chdir"); 
     return -1; 
    } 

    // check if we successfully dropped the root privileges 
    if (setuid(0) == 0 || seteuid(0) == 0) { 
     printf("could not drop root privileges!\n"); 
     return -1; 
    } 

    return 0; 
}