UNIX再学习 -- 用户 ID 和组 ID
生活随笔
收集整理的這篇文章主要介紹了
UNIX再学习 -- 用户 ID 和组 ID
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
用戶 ID和組 ID 的內容已經在好幾章中出現過了。之前都沒有講到,現在放到一起總結。
一、用戶 ID 和 組 ID 回顧
1、我們在APUE 第 4、6、8 章,都有涉及到。
其中我們用到的地方:
(1)修改文件權限
參看:UNIX再學習 -- 文件和目錄
chgrp命令 功能:改變文件或目錄的屬組。
chown 命令 功能:更改某個文件或目錄的屬主和屬組。
# ls -l 總用量 16 -rwxr-xr-x 1 root root 7308 Apr 27 11:28 a.out -rw-r--r-- 1 root root 422 Apr 27 11:28 test.c
第一個 root 為屬主,第二個 root 為屬組。當然這里面沒有用戶 ID,和 組 ID
參看:unix實際用戶ID和有效用戶ID解析
參看:維基百科 -- chmod 命令
下面解析一下格式所表示的意思。這種表示方法一共有十位:
? ? p表示命名管道文件?
? ? d表示目錄文件?
? ? l表示符號連接文件?
? ? -表示普通文件?
? ? s表示socket文件?
? ? c表示字符設備文件?
? ? b表示塊設備文件
第8-6位、5-3位、2-0位分別表示文件所有者的權限,同組用戶的權限,其他用戶的權限,其形式為rwx:
? ? r表示可讀,可以讀出文件的內容
? ? w表示可寫,可以修改文件的內容
? ? x表示可執行,可運行這個程序
? ? 沒有權限的位置用-表示
如果一個文件被設置了 SUID 或 SGID 位,會分別表現在所有者或同組用戶的權限的可執行位上。例如:
? ? -rwsr-xr-x 表示SUID和所有者權限中可執行位被設置
? ? -rwSr--r-- 表示SUID被設置,但所有者權限中可執行位沒有被設置
? ? -rwxr-sr-x 表示SGID和同組用戶權限中可執行位被設置
? ? -rw-r-Sr-- 表示SGID被設置,但同組用戶權限中可執行位沒有被設置
其實在UNIX的實現中,文件權限用12個二進制位表示,如果該位置上的值是
表示有相應的權限:
上面的-rwsr-xr-x的值為: 1 0 0 1 1 1 1 0 1 1 0 1
-rw-r-Sr--的值為: 0 1 0 1 1 0 1 0 0 1 0 0
(2)/etc/passwd?
# cat /etc/passwd root:x:0:0:root:/root:/bin/bash tarena:x:1000:1000:tarena,,,:/home/tarena:/bin/bash 其中 超級用戶 root 的用戶 ID 為 0,組 ID 也為 0 。而我所用的 普通用戶 tarena 用戶 ID 為 1000,組 ID 為 1000 。
(3)函數 stat
stat 結構體中的 st_uid、st_gid 即用戶 ID 和組 ID struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ };(4)口令文件相關函數
參看:UNIX再學習 -- 系統數據文件和信息
函數 getpwuid 和 getpwnam ?函數 getpwent、setpwent 和 endpwent 可用于查看 用戶 ID 和 組 ID
(5)ps 指令部分
參看:UNIX再學習 -- ps、top、kill 指令 其中各列含義如下:USER ? ?用戶名 UID ? ? ? 用戶ID(User ID) # ps lax F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND 4 0 1 0 20 0 3612 2064 poll_s Ss ? 0:01 /sbin/init 1 0 2 0 20 0 0 0 kthrea S ? 0:00 [kthreadd] 1 0 3 2 20 0 0 0 run_ks S ? 0:01 [ksoftirqd/0] 5 0 5 2 20 0 0 0 worker S ? 0:01 [kworker/u:0]
二、獲取調用進程用戶 ID 和 組 ID
當一個用戶通過合法的用戶名和口令登錄系統以后,系統就會為它啟動一個 shell 進程,shell 進程的實際用戶 ID 和實際組 ID 就是該登錄用戶的用戶 ID 和組 ID。該用戶在 shell 下啟動的任何進程都是 shell 進程的子進程,自然也就繼承了 shell 進程的實際用戶 ID 和實際組 ID。1、獲取調用進程的實際用戶 ID 和實際組 ID
#include <unistd.h> #include <sys/types.h> uid_t getuid(void); gid_t getgid(void); 分別返回調用進程的實際用戶ID 和實際組 ID(1)示例說明
#include <stdio.h> #include <unistd.h> #include <sys/types.h>int main (void) {printf ("uid = %d\n", getuid ());printf ("gid = %d\n", getgid ());return 0; } 輸出結果: uid = 0 gid = 0我用的是超級用戶 root 用戶ID為 0,組 ID 為 0 一個進程的用戶和組身份決定了它可以訪問哪些資源,比如讀、寫或者執行某個文件。但真正被用于權限驗證的并不是進程的實際用戶 ID和實際組 ID,而是其有效用戶ID 和有效組 ID。一般情況下,進程的有效用戶 ID和有效組 ID 就取自其實際用戶 ID 和實際組 ID,二者是等價的。2、獲取調用進程的有效用戶 ID 和有效組 ID
#include <unistd.h> #include <sys/types.h> uid_t geteuid(void); gid_t getegid(void); 分別返回調用進程的有效用戶ID 和有效組 ID(1)示例說明
#include <stdio.h> #include <unistd.h> #include <sys/types.h>int main (void) {printf ("euid = %d\n", geteuid ());printf ("egid = %d\n", getegid ());return 0; } 輸出結果: euid = 0 egid = 0三、設置用戶 ID 位和設置組 ID 位
如果用于啟動進程的可執行文件帶有設置用戶 ID 位(SUID)和(或)設置組 ID 位(SGID),那么該進程的有效用戶 ID 和(或)有效組 ID 就不再取自其實際用戶 ID 和(或)實際組 ID,而是取自可執行文件的擁有者用戶 ID 和(或)組 ID。1、什么是設置用戶 ID 位 和設置組 ID 位
參看:“實際用戶ID”、“有效用戶ID”和“保存的設置用戶ID”三個術語的區別是什么?例如:# ls -la test -rwsrwsr-x 1 root root 7314 Apr 27 16:10 test其中第一個 s 為設置用戶 ID 位(SUID),第二個 s 為設置組 ID 位(SGID)設置方法:第一種:采取字符設置sudo chmod u+s test 設置SUID位 sudo chmod g+s test 設置SGID位 sudo chmod u-s test 去掉SUID設置 sudo chmod g-s test 去掉SGID設置第二種:采用數字方式設置:在一般文件權限設置的3位數字前再加上一位數字,成為4位數字。如果該位為4, 則表示設置 setuid?
如果該位為2, 則表示設置 setgid?
所以,假設文件或目錄的原來權限位設置是777,要加上SUID/SGID,如下設置:chmod 4777 test 設置 SUID 位 chmod 2777 test 設置 SGID 位 chmod 0777 test 去掉 SUID 設置 chmod 0777 test 去掉 SGID 設置第三種:使用 chmod 函數再回顧 stat 函數,設置用戶 ID 位及設置組 ID 位都包含在文件的 st_mode 值中。這兩位可分別用常量 S_ISUID 和 S_ISGID 測試。S_ISUID 0004000 set UID bit S_ISGID 0002000 set-group-ID bit (see below) 例如,設置權限 0644:S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
而設置了 SUID 和 SGID 位。即:S_ISUID | S_ISGID | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH舉個例子:#include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> int main (void) { int res = chmod ("a.txt", S_ISUID | S_ISGID | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (-1 == res) perror ("fail to chmod"), exit (1); execlp ("stat", "stat", "a.txt", NULL);return 0; } 輸出結果:文件:"a.txt"大小:0 塊:0 IO 塊:4096 普通空文件 設備:801h/2049d Inode:2107549 硬鏈接:1 權限:(6644/-rwSr-Sr--) Uid:( 0/ root) Gid:( 0/ root) 最近訪問:2017-04-27 18:35:15.952010539 +0800 最近更改:2017-04-27 18:35:15.952010539 +0800 最近改動:2017-04-28 09:26:11.755554531 +0800 創建時間:-而清空使用:~S_ISUID & ~S_ISGID & S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH#include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> int main (void) { int res = chmod ("a.txt", ~S_ISUID & ~S_ISGID & S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (-1 == res) perror ("fail to chmod"), exit (1); execlp ("stat", "stat", "a.txt", NULL);return 0; } 輸出結果:文件:"a.txt"大小:0 塊:0 IO 塊:4096 普通空文件 設備:801h/2049d Inode:2107549 硬鏈接:1 權限:(0644/-rw-r--r--) Uid:( 0/ root) Gid:( 0/ root) 最近訪問:2017-04-27 18:35:15.952010539 +0800 最近更改:2017-04-27 18:35:15.952010539 +0800 最近改動:2017-04-28 09:27:11.747554284 +0800 創建時間:-這個結果是 ?6644/-rwSr-Sr--, 其中 s 和 S 還是有區別的,上面提到 S?表示 SUID/SGID 被設置,但所有者權限中可執行位沒有被設置。因為設置 SUID/SGID 需要有運行權限。如果簡單的用 chmod 函數來設置好像還是不行。所以不推薦使用第三種方法。
找出當前設置了 suid 的文件方法:參看:C語言再學習 -- Linux下find命令用法
# find . -perm -4000 -type f ./a.txt 2、SUID 和 SGID 位 作用舉個例子,用戶更改登錄密碼,是通過運行命令passwd來實現的。最終必須要修改/etc/passwd文件,而passwd的文件的屬性是:# stat /etc/passwd文件:"/etc/passwd"大小:1918 塊:8 IO 塊:4096 普通文件 設備:801h/2049d Inode:1076219 硬鏈接:1 權限:(0644/-rw-r--r--) Uid:( 0/ root) Gid:( 0/ root) 最近訪問:2017-04-28 09:36:00.775552859 +0800 最近更改:2016-12-01 10:28:54.801942085 +0800 最近改動:2016-12-01 10:28:54.809942031 +0800 創建時間:- 我們可以看到 passwd 文件只有對于 root 用戶是可寫的,而對于所有的他用戶來說都是沒有寫權限的。 那么一個普通的用戶如何能夠通過運行 passwd 命令修改這個 passwd 文件呢?為了解決這個問題,SUID/SGID 便應運而生。
當一個程序設置了為SUID位時,內核就知道了運行這個程序的時候,應該認為是文件的所有者在運行這個程序。即該程序運行的時候,有效用戶ID是該程序的所有者。
有效用戶ID和有效組ID則決定了進程在運行時的權限。內核在決定進程是否有文件存取權限時,是采用了進程的有效用戶ID來進行判斷的。
雖然你以 tarena t登陸系統,但是當你輸入 passwd 命令來更改密碼的時候,由于 passwd 設置了 SUID 位,因此雖然進程的實際用戶 ID 是 tarena 對應的 ID,但是進程的有效用戶 ID 則是 passwd 文件的所有者 root 的 ID,因此可以修改 /etc/passwd 文件。
3、有效用戶/組 ID 和設置用戶/組/ ID 位關系假設 test 文件的擁有這用戶和組都是 root ,且其他用戶對該文件可執行權限。# ls -la test -rwxr-xr-x 1 root root 7238 Apr 27 15:35 test而此時我注銷root用戶,使用隸屬于 tarena 組 (GID = 1000)和tarena 用戶(UID = 1000)登錄系統,運行 test 程序。#include <stdio.h> #include <unistd.h> #include <sys/types.h>int main (void) {printf ("uid = %d\n", getuid ());printf ("gid = %d\n", getgid ());printf ("euid = %d\n", geteuid ());printf ("egid = %d\n", getegid ());return 0; } 輸出結果: uid = 1000 gid = 1000 euid = 1000 egid = 1000 可以看到進程的有效用戶 ID 和實際用戶 ID 一樣都是 1000,而有效組 ID 也和實際組 ID一樣都是 1000。它們都是取自可執行文件的擁有者用戶 ID 和(或)組 ID。現在 root 用戶為 test 文件添加設置用戶 ID 位和設置組 ID 位權限位。也必須使用 sudo 普通用戶無法修改權限。設置權限 sudo chmod u+s test sudo chmod g+s test查看 test 權限 $ ls -la test -rwsrwsr-x 1 root root 7314 4月 27 16:10 test使用隸屬于 tarena 組和 tarena用戶再次運行 test 程序#include <stdio.h> #include <unistd.h> #include <sys/types.h>int main (void) {printf ("uid = %d\n", getuid ());printf ("gid = %d\n", getgid ());printf ("euid = %d\n", geteuid ());printf ("egid = %d\n", getegid ());return 0; } 輸出結果: uid = 1000 gid = 1000 euid = 0 egid = 0不難發現,進程的實際用戶 ID 和實際組 ID 并沒有發生變化,仍然是 1000,但它的有效用戶 ID 和有效組 ID 卻變成了 0,顯然這是 test 文件的擁有者 root 用戶 ID 和 組 ID,而參與權限判斷,決定該進程能做什么不能做什么的恰恰是它的有效用戶 ID 和有效組 ID,tarena 用戶扮演 root 用戶行使權限。
四、實際用戶 ID和有效用戶 ID 區別
上面我們有簡單的介紹了下,實際用戶 ID 和有效用戶 ID的。 參看:Linux進程的實際用戶ID和有效用戶ID (1)實際用戶ID和實際用戶組ID 標識我是誰。也就是登錄用戶的uid和gid,比如我的 Linux 以 tarena 登錄,在Linux運行的所有的命令的實際用戶ID都是 tarena 用戶組ID都是 tarena 的gid(可以用id命令查看)。(2)有效用戶ID和有效用戶組ID 進程用來決定我們對資源的訪問權限。一般情況下,有效用戶ID等于實際用戶ID,有效用戶組ID等于實際用戶組ID。當設置用戶 ID(SUID)位設置,則有效用戶ID等于文件的所有者的uid,而不是實際用戶ID;同樣,如果設置了設置用戶組 ID(SGID)位,則有效用戶組ID等于文件所有者的gid,而不是實際用戶組ID。
這里介紹一個命令 ?id ,參看:id 命令 功能: id 命令可以顯示真實有效的用戶 ID(UID)和組(ID)。 選項: -g或--group 顯示用戶所屬群組的ID。 -G或--groups 顯示用戶所屬附加群組的ID。 -n或--name 顯示用戶,所屬群組或附加群組的名稱。 -r或--real 顯示實際ID。 -u或--user 顯示用戶ID。 -help 顯示幫助。 -version 顯示版本信息。 示例: //超級用戶 root 登錄 # id uid=0(root) gid=0(root) 組=0(root)//普通用戶 tarena 登錄 $ id uid=1000(tarena) gid=1000(tarena) 組=1000(tarena),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
五、更改用戶 ID 和更改組 ID
在 UNIX 系統中,特權(如能改變當前日期的表示法)以及訪問控制(如能否讀、寫一個特定文件),是基于用戶 ID 和組 ID 的。當程序需要增加特權,或需要訪問當前并不允許訪問的資源時,我們需要更換自己的用戶 ID 和組 ID,使得新 ID 具有合適的特權或訪問權限。與此類此,當程序需要降低其特權或阻止對某些資源的訪問時,也需要更換用戶 ID 或組 ID,新 ID 不具有相應特權或訪問這些資源的能力。1、我們可以用 setuid 函數設置實際用戶 ID 和有效用戶 ID。可以用 setgid 函數設置實際組 ID 和有效組 ID。
#include <sys/types.h> #include <unistd.h> int setuid(uid_t uid); int setgid(gid_t gid); 兩個函數返回值,若成功,返回 0,;若出錯,返回-1(1)函數解析
關于誰能更改 ID 有若干規則。現在先考慮更改用戶 ID 的規則(關于用戶 ID 我們所說明的一切都適用于組 ID)。 1)若進程具有超級用戶特權,則 setuid 函數將實際用戶 ID、有效用戶 ID 以及保存的設置用戶 ID(SUID) 設置為 uid。? 2)若進程沒有超級用戶特權,但是 uid 等于實際用戶 ID 或保存的設置用戶 ID,則 setuid 只將有效用戶 ID 設置為 uid。不更改實際用戶 ID 和保存的設置用戶 ID。 3)如果上面兩個條件都不滿足,則 errno 設置為 EPERM,并返回 -1 在此假定 _POSIX_SAVED_IDS 為真。如果沒有提供這種功能,則上面所說的關于保存的設置用戶 ID 部分都無效。關于內核所維護的 3 個用戶 ID,還要注意以下幾點。 1)只有超級用戶進程可以更改實際用戶 ID。通常,實際用戶 ID 是在用戶登錄時,由 login 程序設置的,而且決不會改變它。因為 login 是一個超級用戶進程,當它調用 setuid 時,設置所有 3 個用戶 ID。 2)僅當對程序文件設置了用戶 ID 位時,exec 函數才設置有效用戶 ID。如果設置用戶 ID 位沒有設置,exec 函數不會改變有效用戶 ID,而將維持其現有值。任何時候都可以調用 setuid ,將有效用戶 ID 設置為實際用戶 ID 或保存的設置用戶 ID。自然地,不能將有效用戶 ID 設置為任一隨機值。 3)保存的設置用戶 ID 是由 exec 復制有效用戶 ID 而得到的。如果設置了文件的設置用戶 ID 位,則在 exec 根據文件的用戶 ID 設置了進程的有效 ID 以后,這個副本就被保存起來了。
總結,更改 3 個用戶 ID 的不同方法:
(2)示例說明
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <stdlib.h>int main (void) {printf ("uid = %d\n", getuid ());printf ("gid = %d\n", getgid ());printf ("euid = %d\n", geteuid ());printf ("egid = %d\n", getegid ());if(!setgid(1234))printf("setgid successfully!\n");elseperror ("setgid"), exit (1);if(!setuid(1234))printf("setuid successfully!\n");elseperror ("setuid"), exit (1);printf ("--------------------\n");printf ("uid = %d\n", getuid ());printf ("gid = %d\n", getgid ());printf ("euid = %d\n", geteuid ());printf ("egid = %d\n", getegid ());return 0; } 輸出結果: uid = 0 gid = 0 euid = 0 egid = 0 setgid successfully! setuid successfully! -------------------- uid = 1234 gid = 1234 euid = 1234 egid = 1234(3)示例解析
考慮一個問題,為什么我先使用 setgid,然后再使用 setuid? 如果反過來會出現?setgid: Operation not permitted。2、函數 seteuid 和 setegit
#include <unistd.h> int seteuid(uid_t euid); int setegid(gid_t egid); 兩個函數返回值,若成功,返回 0,失敗返回 -1(1)函數解析
它們類似于 setuid 和 setgid,但是只更改有效用戶 ID 和有效組 ID。 一個非特權用戶可將其有效用戶 ID 設置為實際用戶 ID 或保存設置用戶 ID。對于一個特權用戶則可將有效用戶 ID 設置為 uid。這區別于 setuid 函數,它更改所有 3 個用戶 ID。(2)示例說明
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <stdlib.h> int main (void) { printf ("uid = %d\n", getuid ()); printf ("gid = %d\n", getgid ()); printf ("euid = %d\n", geteuid ()); printf ("egid = %d\n", getegid ()); if(!setegid(1234)) printf("setegid successfully!\n"); else perror ("setegid"), exit (1); if(!seteuid(1234)) printf("seteuid successfully!\n"); else perror ("seteuid"), exit (1); printf ("--------------------\n"); printf ("uid = %d\n", getuid ()); printf ("gid = %d\n", getgid ()); printf ("euid = %d\n", geteuid ()); printf ("egid = %d\n", getegid ()); return 0; } 輸出結果: uid = 0 gid = 0 euid = 0 egid = 0 setegid successfully! seteuid successfully! -------------------- uid = 0 gid = 0 euid = 1234 egid = 12343、函數 setreuid 和 setregid
#include <sys/types.h> #include <unistd.h> int setreuid(uid_t ruid, uid_t euid); int setregid(gid_t rgid, gid_t egid); 兩個函數返回值,若成功,返回 0;若出錯,返回 -1(1)函數解析
函數功能是交換實際用戶 ID 和有效用戶 ID 的值。 如若其中任一參數的值為 -1,則表示相應的 ID 保持不變。 規則很簡單:一個非特權用戶總能交換實際用戶 ID 和有效 ID。這就允許一個設置用戶 ID 程序交換成用戶的普通權限,以后又可再次交換用戶 ID 權限。POSIX.1 引進了保存的設置用戶 ID 特性后,其規則也相應的加強,它允許了一個非特權用戶將其有特權用戶 ID 設置為 保存的設置用戶 ID。(2)示例說明
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <stdlib.h> int main (void) { printf ("uid = %d\n", getuid ()); printf ("gid = %d\n", getgid ()); printf ("euid = %d\n", geteuid ()); printf ("egid = %d\n", getegid ()); if(!setregid(0, 1234)) printf("setregid successfully!\n"); else perror ("setregid"), exit (1); if(!setreuid(0, 1234)) printf("setreuid successfully!\n"); else perror ("setreuid"), exit (1); printf ("--------------------\n"); printf ("uid = %d\n", getuid ()); printf ("gid = %d\n", getgid ()); printf ("euid = %d\n", geteuid ()); printf ("egid = %d\n", getegid ()); return 0; } 輸出結果: uid = 0 gid = 0 euid = 0 egid = 0 setregid successfully! setreuid successfully! -------------------- uid = 0 gid = 0 euid = 1234 egid = 12344、設置不同用戶 ID 的各個函數關系
六、APUE 第 8 章未講部分
第八章是我總結 APUE 以來講的時間最長的一章,各種小知識點,各種不熟悉,未講部分一大串,甚是無語。 函數 waitid 未講 函數 wait3 和 wait4 未講 競爭條件 未講 解釋器文件 未講 ?(后續添加到,shell編程這篇文章里,參看:UNIX再學習 -- shell編程) 進程會計 未講 用戶標識 未講 進程調度 未講 進程時間 未講 (這部分查看時間函數,參看:C語言再學習 -- 時間函數)總結
以上是生活随笔為你收集整理的UNIX再学习 -- 用户 ID 和组 ID的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022年中国美妆护肤品行业投资研究报告
- 下一篇: 锁、事务和同步