linux 中的用戶ID (ZZ)

一、進程與權限

A.進程時Linux/Unix操作系統中最重要的抽象之一

B.進程是一個處於執行期的程序(目標代碼存儲在某種介質上)
A process is a program(object code stored on some media) in the midst of
execution.

而進程在執行過程中經常涉及到文件訪問操作等
1.一個運行中的進程究竟可以訪問哪些資源,而不能訪問哪些資源?
2.一個進程運行過程中如何實現在某個執行階段擁有一些權限而在另一個階段又具備另外一種權限呢?

上述狀況對應於進程的訪問權限以及進程執行中不同權限的切換。這一切都和進程實際用戶ID,進程有效用戶ID,進程保存設置用戶ID有關聯。

二、進程的用戶ID

1.實際用戶ID(real user id,RUID)

爲該進程的創建者的用戶ID,也可以說是進程的執行者。該ID僅root用戶可以修改

2.有效用戶ID(effective user id,EUID)

該ID用戶標識用戶進程執行操作的權限。例如:如果EUID是0即(root),此時進程擁有root用戶權限。普通用戶可以將EUID設置爲RUID或者SUID,而超級用戶可以將EUID設置爲任意的合法UID。

注意:對於一個可執行的二進制文件,如果其set-user-id bit被設置,則運行改程序時,對應進程的有效用戶ID(EUID)爲該文件的文件所有者用戶ID。

3.保存設置用戶ID(saved set-user-id,SUID)

對於沒有設置set-uid-bit的可執行程序而言,其對應進程的保存設置用戶ID(SUID)爲其實際用戶ID;而對於設置了set-uid-bit的程序而言,其對應的保存設置用戶ID(SUID)爲該可執行文件的文件擁有者用戶ID。只能root用戶權限,才能更改(當一個程序文件運行的時候,其值已經確定)。

注意:linux不提供返回保存的設置-用戶-ID的函數

總結:
RUID代表此進程是哪個用戶創建的, EUID代表此進程所擁有的權限,SUID保存了EUID,當EUID改變後,如果想回到以前的EUID,此時SUID將發揮作用。

三、相關API

#include <sys/types.h>
#include <unistd.h>

int setuid(uid);

可以用setuid函數設置實際用戶ID和有效用戶ID。注意,我們並不能想怎麼設就怎麼設。有若干規則需要我們遵守:

(1)若進程具有超級用戶特權,則setuid函數將實際用戶ID、有效用戶ID,以及保存的設置-用戶-D設置爲uid。

(2)若進程沒有超級用戶特權,但是uid等於實際用戶ID或保存的設置-用戶-ID,則setuid只將有效用戶ID設置爲。不改變實際用戶ID和保存的設置-用戶-ID。

(3)如果上面兩個條件都不滿足,則errno設置爲EPERM,並返回出錯.。

關於內核所維護的三個用戶ID,還要注意下列幾點

(1)只有超級用戶進程可以更改實際用戶ID。通常,實際用戶ID是在用戶登錄時,由login(1)程序設置的,而且決不會改變它。因爲login是一個超級用戶進程,當它調用setuid時,設置所有三個用戶ID。

(2)僅當對程序文件設置了 set-user-id bit時,exec函數設置有效用戶ID爲文件所有者。如果set-user-id bit沒有設置,則exec函數不會改變有效用戶ID,而將其位置原先值。任何時候都可以調用setuid,將有效用戶ID設置爲實際用戶ID或保存的設置-用戶-ID。自然,不能講有效用戶ID設置爲任意隨機值。

四、探究

案例一:
有個要在MacOS上實現一個關機程序,它熟悉linux,其實老祖宗都是unix,所以MacOS對他並不陌生。如下他寫了如下程序實現關機



編譯運行:



從上面看,這段代碼可以實現關機,但是有個不符合要求的地方,運行程序不能是超級用戶,只能是普通用戶。

他很苦惱,後來找到了我,我做了如下事情



呵呵,可以了。我將這個可執行文件的owner和group都改爲了root,然後有講其set-user-id bit位加以了設置。這樣以普通用戶運行這個程序時,此進程的EUID就是root了,這樣滿足了shutdown命令執行的權限。

案例二:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>

  7. void test_read_file(const char *name)
  8. {
  9. int fd = -1;

  10. fd = open(name ,O_RDWR);
  11. if(fd < 0){
  12. printf("=[ERROR]:read failed.\n");
  13. }else{
  14. printf("=[OK]:read successful\n");
  15. close(fd);
  16. }
  17. }

  18. //打印uid和euid
  19. void p_states()
  20. {
  21. int uid = 0;
  22. int euid = 0;

  23. printf("------Current states--------\n");
  24. printf("real uid\t %d\n",getuid());
  25. printf("effective uid\t %d\n",geteuid());
  26. printf("----------------------------\n");
  27. }

  28. //調用setuid
  29. void run_setuid_fun(int uid)
  30. {
  31. if(setuid(uid) == -1)
  32. {
  33. printf("=[ERROR]:setuid(%d) error\n",uid);
  34. }

  35. p_states();
  36. }

  37. //調用setuid
  38. void run_seteuid_fun(int uid)
  39. {
  40. if(setuid(uid) == -1)
  41. {
  42. printf("=[ERROR]:seteuid(%d) error\n",uid);
  43. }

  44. p_states();
  45. }

  46. int main()
  47. {
  48. int t_re = 0;
  49. const char *file = "root_only.txt";

  50. printf("\nTEST 1:\n");
  51. p_states();
  52. //此時real uid = login user id
  53. //effective uid = root
  54. //saved uid = root
  55. test_read_file(file);

  56. getchar();

  57. printf("\nTEST 2:setuid(getuid())\n");
  58. run_seteuid_fun(getuid());
  59. //此時real uid = login user id
  60. //effective uid = login user id
  61. //saved uid = root
  62. test_read_file(file);

  63. getchar();

  64. printf("\nTEST 3:setuid(0)\n");
  65. run_setuid_fun(0);
  66. //此時real uid = login user id
  67. //effective uid = root
  68. //saved uid = root
  69. test_read_file(file);

  70. getchar();

  71. printf("\nTEST 4:setuid(0)\n");
  72. run_setuid_fun(0);
  73. //此時real uid = root
  74. //effective uid = root
  75. //saved uid = root
  76. test_read_file(file);

  77. getchar();

  78. printf("\nTEST 5:setuid(503)\n");
  79. run_setuid_fun(503);
  80. //此時real uid = login user id
  81. //effective id = login user id
  82. //saved uid = login user id
  83. test_read_file(file);

  84. getchar();

  85. printf("\nTEST 6:setuid(0)\n");
  86. //read uid = login user id
  87. //effective uid = login user id
  88. //saved uid = login user id
  89. run_setuid_fun(0);
  90. test_read_file(file);

  91. return 0;
  92. }
編譯運行:



root_only.txt文件建立:



第一次:

RUID:實際用戶
EUID:文件所有者(root)
SUID:文件所有者(root)



此時進程擁有root用戶權限,能對root_only.txt進行讀寫操作

第二次:

此時普通用戶調用setuid(getuid()),只會將EUID改爲getuid(),其他都不變
RUID:實際用戶
EUID:實際用戶
SUID:root



此時進程沒有有root用戶權限,不能能對root_only.txt進行讀寫操作

第三次:

此時普通用戶調用seteuid(0),只會將EUID改爲0,其他都不變
RUID:實際用戶
EUID:root
SUID:root



此時進程擁有root用戶權限,能對root_only.txt進行讀寫操作

第四次:

此時進程擁有root用戶權限,調用setuid(0),會將三個ID都設置爲0
RUID:root
EUID:root
SUID:root



此時進程擁有root用戶權限,能對root_only.txt進行讀寫操作

第五次:

此時進程擁有root用戶權限,調用setuid(503),會將三個ID都設置爲503
RUID:503
EUID:503
SUID:503



此時進程擁有普通用戶權限,不能對root_only.txt進行讀寫操作

第六次:

此時進程擁有普通用戶權限,調用setuid(0),此時RUID,SUID都不爲0,這一次操作將失敗
RUID:503
EUID:503
SUID:503


此時進程擁有普通用戶權限,不能能對root_only.txt進行讀寫操作
發佈了26 篇原創文章 · 獲贊 9 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章