真實用戶id、有效用戶id、保存的設置用戶id的區別以及聯繫(setuid、seteuid)

文章轉載自:https://blog.csdn.net/taiyang1987912/article/details/40651623

 

在使用 setuid() 函數時會遇到 3 個關於 ID 的概念:
real user ID -- 真實用戶 ID
effective user ID -- 有效用戶 ID
saved set-user-ID -- 保存了的設置用戶 ID。

真實用戶 ID (real user ID) 就是通常所說的 UID,在 /etc/passwd 中能看到它的身影,如:

beyes:x:1000:1000:beyes,206,26563288,63230688:/home/beyes:/bin/bash

它用來標識系統中各個不同的用戶。普通用戶無法改變這個 ID 值。

有效用戶 ID (effective) 表明,在運行一個程序時,你是以哪種有效身份運行它的。一般而言,有效用戶ID 等於 真實用戶 ID。這兩個 ID 值可以用 geteuid() 和 getuid() 函數獲取。

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>


int main(void)

{

printf ("The real user ID is: %d\n", getuid());

printf ("The effective user ID is :%d\n", geteuid());

return (0);

}

編譯程序後,查看生成的可執行文件權限:

 

$ls -l getuid.exe
-rwxr-xr-x 1 beyes beyes 4775 Jun  9 15:38 getuid.exe

普通用戶運行

$ ./getuid.exe
The real user ID is: 1000
The effective user ID is :1000

root 用戶運行

# ./getuid.exe
The real user ID is: 0
The effective user ID is :0

這就是所說的一般情況:實際用戶ID == 有效用戶 ID

下面看不一致的情況
使用 chmod 改變該程序的有效權限位(suid):

$ chmod u+s getuid.exe
$ ls -l getuid.exe
-rwsr-xr-x 1 beyes beyes 4775 Jun  9 15:38 getuid.exe


再使用普通用戶運行:

$ ./getuid.exe
The real user ID is: 1000
The effective user ID is :1000

切換到 root 用戶運行:

# ./getuid.exe
The real user ID is: 0
The effective user ID is :1000

從 root 運行輸出來看,有效用戶 ID 的值爲 1000 。也就是說,root 運行這個程序時,是沒有 root 的權限的,它只有 beyes 這個普通用戶(實際用戶 ID 爲 1000)的權限。關於設置有效權限位可以參考:http://www.groad.net/bbs/read.php?tid-367.html

下面用一個實例來驗證 root 運行此程序時只有 beyes 這個普通用戶的權限,按下面步驟來進行:

1. 現在 /root 目錄下創建一個普通的文本文件 rootfile.txt :

# echo "hello world" > /root/rootfile.txt
# ls -l /root/rootfile.txt
-rw-r--r-- 1 root root 12 Jun  9 15:48 /root/rootfile.txt


2. 修改上面的程序代碼爲:

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>


int main(void)

{

    const char *file = "/root/rootfile.txt";


    printf ("The real user ID is: %d\n", getuid());

    printf ("The effective user ID is :%d\n", geteuid());


    if (!unlink (file))

        printf ("Ok, I am root, and I can delete the file which in /root directory.\n");

    else

        perror ("unlink error");


    return (0);

}

在上面的代碼中所要做的事情很簡單,就是要嘗試刪除 /root/rootfile.txt 這個文件。使用普通用戶來編譯該程序。

現在先使用普通用戶來運行該程序:

$ ./getuid.exe
The real user ID is: 1000
The effective user ID is :1000
unlink error: Permission denied

很自然,普通用戶沒有權限刪除 /root 下的文件。

下面使用 root 來運行該程序:

# ./getuid.exe
The real user ID is: 0
The effective user ID is :0
Ok, I am root, and I can delete the file which in /root directory.

也很正常,root 有權限刪除它目錄下的文件。

現在爲這個程序添加有效權限位,注意這裏是用普通用戶運行的 chmod 命令:

beyes@debian:~$ chmod u+s getuid.exe
beyes@debian:~$ ls -l getuid.exe
-rwsr-xr-x 1 beyes beyes 5211 Jun 10 10:45 getuid.exe

再次分別以 普通用戶 和 root 用戶運行上面的程序:

普通用戶:

$ ./getuid.exe
The real user ID is: 1000
The effective user ID is :1000
unlink error: Permission denied

還是一樣,普通用戶並沒有權限刪除 /root 下的文件。

root 用戶:

# ./getuid.exe
The real user ID is: 0
The effective user ID is :1000
unlink error: Permission denied

由輸出可見,root 用戶也沒法刪除該文件!同時我們看到,此時的 有效用戶 ID 值爲 1000,所以我們知道,這個程序所賦予操作文件的權限不但要檢查實際用戶ID,更重要的是要考慮 有效用戶ID ;如果 有效用戶 沒有權限刪除,那麼換成 root 用戶它也相當於被降權了,當然這個降權僅限於在這個程序的空間中。



下面通過 setuid() 和 seteuid() 這兩個函數來考察一下 saved set-user-ID (保存的設置用戶ID)這個概念。

在使用 setuid() 時會遇到如下情況:
1. 若進程有 root 權限,則函數將實際用戶 ID、有效用戶 ID 設置爲參數 uid  。見如下代碼:

 

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>


void show_ids(void)

{

    printf ("real uid: %d\n", getuid());

    printf ("effective uid: %d\n", geteuid());

}


int main(int argc, char *argv[])

{


    int uid;

    show_ids();

    uid = atoi(argv[1]);


    if (setuid (uid) < 0)

        perror ("setuid error");


    show_ids();


    return (0);

}

下面使用 root 用戶來運行上面的程序:

 

# ./setuid.exe 1001
real uid: 0
effective uid: 0
real uid: 1001
effective uid: 1001

由此可見,在 root 下,實際用戶 ID 和有效用戶 ID 均被設爲 setuid() 的參數 uid 的值。

2. 若進程不具有 root 權限,那麼普通用戶使用 setuid() 時參數 uid 只能是自己的,沒有權限設置別的數值,否則返回失敗:
使用普通用戶來運行:

$ ./setuid.exe 1001
real uid: 1000
effective uid: 1000
setuid error: Operation not permitted
real uid: 1000
effective uid: 1000

由以上可以看出,只有超級用戶進程才能更改實際用戶 ID 。所以一個非特權用戶進程不能通過 setuid 或 seteuid 得到特權用戶權限。

這裏考慮 su 這個程序,su 可以將普通用戶切換到 root 用戶。這是因爲,su 被設置了 有效權限位:

# ll /bin/su
-rwsr-xr-x 1 root root 29152 Feb 16 04:50 /bin/su

如上面所做實驗描述的一樣,普通用戶在運行 su 時,它也就擁有了 root 的權限。

對於調用了 setuid() 函數的程序要格外小心,當進程的有效用戶 ID 即 euid 是 root 用戶時(設置了有效權限位),如果調用了 setuid() ,那麼它會與它相關的所有 ID (real user ID, effective user ID,saved set-user-ID) 都變成它參數裏所設的 ID,這樣該進程就變成了普通用戶進程,也就再也恢復不了 root 權限了。看下面代碼:

 

 

 
#include <stdio.h>

#include <stdlib.h>


void show_ids (void)

{

    printf ("The real user ID is: %d\n", getuid());

    printf ("The effective user ID is :%d\n", geteuid());

}


int main(void)

{

    const char *file = "/root/rootfile3.txt";  

        setuid (0)

    show_ids();

    if (!unlink (file)) {

        printf ("Ok, I am root, and I can delete the file which in /root directory.\n");

    system ("echo hello world > /root/rootfile3.txt");

    printf ("Now, drop the root privileges.\n");
    
    if (setuid (1000) < 0) {

        perror ("setuid");

    exit (EXIT_FAILURE);

    }

    show_ids();

    if (unlink (file) < 0) {

        printf ("Ok, we have no privilege to delete rootfile.txt.\n");

    }

    printf ("try to regain root power again...\n");

    if (seteuid (0)) {

        perror ("seteuid");

    show_ids();

    exit (EXIT_FAILURE);

    }

}

 

我們使用 root 編譯上面的程序,並運行 chmod u+s 給程序添加 suid 位,然後以普通用戶來運行它:

# ./getuid3 The real user ID is: 0The effective user ID is :0Ok, I am root, and I can delete the file which in /root directory.Now, drop the root privileges.The real user ID is: 1000The effective user ID is :1000Ok, we have no privilege to delete rootfile.txt.try to regain root power again...seteuid: Operation not permittedThe real user ID is: 1000The effective user ID is :1000

由輸出可見,在運行 setuid (1000) 函數時,我們還是具有 root 權限的,所以該函數會設置成功。正是因爲有了 root 權限,所以 3 個 ID (真實用戶ID,已保存用戶ID,有效用戶ID)都會被設置爲 1000。所以在運行完 setuid(1000) 後,進程已經被降權爲普通用戶,此時想再  seteuid (0) 提高權限已經不可能。這裏需要提到一點,對於 show_ids() 函數裏,我們無法獲取 保存的設置用戶ID(saved set-user-ID),這是因爲沒有這種 API 。但是我們知道這個約定:當用戶是 root 時,使用 setuid() 來修改 uid,這 3 個 ID 是會被同時都修改的。但是有沒有辦法,先使進程降權後在某些時候再恢復 root 權力呢?辦法是使用 seteuid() 而不是 setuid() 。那 setuid() 和 seteuid() 有什麼不同麼?在 seteuid() 的 man 手冊裏提到:

seteuid() sets the effective user ID of the calling process.  Unprivileged user processes may only set the effective user ID to the real user ID, the effec‐tive user ID or the saved set-user-ID.

setedui() 用來設置調用進程的有效用戶 ID。普通用戶進程只能將 有效用戶ID 設置爲 實際用戶ID,有效用戶ID,保存的設置用戶ID。這裏比較重要的是,seteuid() 中的參數可以被設置爲 保存的設置用戶 ID 。保存的設置用戶 ID 是這樣的一種概念:它是從 exec 複製有效用戶 ID 而得來的。具體的說,當我們從一個 shell 裏執行一個外部命令時(這裏就當做是執行上面的 getuid3 這個),如果該程序設置了用戶ID位(有效權限位),那麼在 exec 根據文件的用戶ID設置了進程的有效用戶 ID 後,就會將這個副本保存起來。簡單的說,saved set-user-ID 保存了 有效用戶ID 的值。比如對於 getuid3 這個程序,saved set-user-ID 保存的值就是 0 。據此,我們修改上面的 getuid3 程序代碼爲:

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>


void show_ids (void)

{

    printf ("The real user ID is: %d\n", getuid());

    printf ("The effective user ID is :%d\n", geteuid());

}


int main(void)

{

    const char *file = "/root/rootfile3.txt";


    show_ids();

    if (!unlink (file)) {

        printf ("Ok, I am root, and I can delete the file which in /root directory.\n");

        system ("echo hello world > /root/rootfile3.txt");

        printf ("Now, drop the root privileges.\n");

        if (seteuid (1000) < 0) {

            perror ("setuid");

            exit (EXIT_FAILURE);

        }

        show_ids();

        if (unlink (file) < 0) {

            printf ("Ok, we have no privilege to delete rootfile3.txt.\n");

           }

    printf ("try to regain root power again...\n");

    if (seteuid (0)) {

        perror ("seteuid");

        show_ids();

        exit (EXIT_FAILURE);

        }

    }

    show_ids();


    printf ("try to delete rootfile3.txt again\n");

    if (!unlink(file)) {

        printf ("Ok, regain root power successful!\n");

        system ("echo hello world > /root/rootfile3.txt");

        return (0);

        }


    return (0);

}

在上面的代碼中,我們將原來的 setuid(1000) 替換爲 seteuid(1000); 。並且在此後,再次嘗試刪除 /root/rootfile3.txt 這個文件。下面在普通用戶下運行該程序:

beyes@debian:~/C/syscall/getuid$ ./getuid3
The real user ID is: 1000
The effective user ID is :0
Ok, I am root, and I can delete the file which in /root directory.
Now, drop the root privileges.
The real user ID is: 1000
The effective user ID is :1000
Ok, we have no privilege to delete rootfile3.txt.
try to regain root power again...
The real user ID is: 1000
The effective user ID is :0
try to delete rootfile.txt again
Ok, regain root power successful!

此時我們看到整個過程:
先是普通用戶執行了具有 root 有效權限位設置的程序,它成功的刪除了 /root 下面的一個文本文件;然後使用 system() 系統調用恢復了該文件,目的是方便下面繼續實驗。接着,它使用 seteuid() 函數時該進程降權爲普通用戶權限進程。此後,正是因爲有了 saved set-user-ID 的保存,所以當再次使用 seteuid() 恢復 進程的 root 權限時可以恢復成功!

所以再次看到,setuid() 會改變 saved set-user-ID 的值而不能恢復權限;而 seteuid() 不會改變 saved set-user-ID 這個值,所以它能夠恢復。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章