tft每日頭條

 > 科技

 > linux存儲管理策略

linux存儲管理策略

科技 更新时间:2025-01-09 00:20:08

Linux 下的文件權限管理分為三組:擁有者、組、其它用戶,文件權限分為讀、寫、執行,但其實并不僅僅如此,還有 setuid、setgid、sticky bit 這一組标志,本文通過一個可執行文件的權限 4755 展開介紹 setuid、setgid 和 sticky bit 的概念,希望本文對讀者理解 Linux 文件權限管理能有所幫助。

歡迎訪問我的博客:https://whowin.gitee.io


1. 概述

  • 本文通過一個 4755 權限的可執行文件引出了關于 Linux 下與文件權限相關的一系列概念,包括 uid、euid、gid、egid、setuid、setgid等等;
  • 希望通過本文讀者可以更好地理解 Linux 下的權限管理方式;
  • 本文涉及的概念并不複雜,所需的關聯知識也不多,希望對初學者和有一定功底的程序員均能有所幫助;
  • Linux 的文件有權限(Permission)和屬性(Attribute),前者用于限制用戶對文件的讀、寫和執行操作,後者也是用于限制對文件的操作,比如限制文件隻能追加、限制文件不能壓縮存放等,權限(Permission)和屬性(Attribute)在中文裡有些混淆,本文僅讨論文件的權限(Permission)。

2. 問題的提出

前不久在折騰 openwrt 時,需要給 openwrt 裝上 sudo(openwrt 默認是不安裝 sudo 的),安裝很成功,用起來也沒什麼問題,但是重啟了以後就不能用了,後來發現原因之一是這個 openwrt 在啟動的時候将 /usr/bin/ 目錄下的所有文件的權限(permission)都改為了 0755,這是一個正常的可執行文件的權限(permission),但是對 sudo 這個可執行文件卻是不行的,sudo 的文件權限(permission)必須是 4755;

解決這個問題倒是不難,在 openwrt 啟動時,執行一下 chmod 4755 /usr/bin/sudo 就行了;

這件事讓我想寫這篇文章,因為我們看慣了諸如 0755、0750等可執行文件屬性,這個 4755 的文件屬性并不多見。


3. 有哪些文件的屬性是4755

  • 4755 的文件屬性在文件列表時的樣子

ls -al /usr/bin/sudo

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)1

圖1:4755的文件屬性怎麼顯示

可以看到平時常見的 rwx 中的 x 變成了 s,這個 s 正是 chmod 4755 /usr/bin/sudo 命令中,那個 “4” 造成的

  • sudo 這個文件有什麼與衆不同的特點

假定我們有一個用戶 demo,我們已經把這個用戶加入到了 sudoers 中,那麼 demo 用戶使用 sudo 命令就可以提權了;

sudo 這個可執行文件的擁有者(owner)一定是 root,執行這個文件時,至少需要讀取用戶密碼文件 /etc/shadow,這個密碼文件的擁有者是 root,權限是 640,所以讀取這個文件是要有 root 權限,也就是說,執行 sudo 是需要有 root 權限的;

問題是,當我們執行這個文件時,當前用戶是 demo,而 demo 是沒有 root 權限的,隻有執行 sudo 經過提權才能短暫地擁有 root 權限;

這裡産生了一個無法調和的矛盾,demo 用戶需要通過執行 sudo 獲得 root 權限,但執行 sudo 也需要 root 權限;

這就是為什麼 sudo 這個文件的屬性是 4755 的原因了,當一個可執行文件的屬性是 4755 時,任何用戶執行這個文件時,将被以該文件擁有者的權限運行,這個問題後面會有詳細的描述;

  • 有哪些文件的屬性是 4755

下面指令可以顯示 /usr/bin/ 目錄下哪些文件的屬性是 4755

ls -al /usr/bin|grep "^-rws"

在我的 ubuntu 下會顯示出這些文件

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)2

圖2:/usr/bin/目錄下4755屬性的文件

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)3

圖3:/bin/目錄下4755屬性的文件

還可以用 find 命令查找系統中所有的權限為 4755 的文件

sudo find / -perm 4755 -type f

4. uid、euid、gid、egid

  • 當我們在 Linux 上創建一個新用戶時,系統會分配給這個用戶一個 id,我們稱為 uid;還會分配給這個用戶一個組 id,我們稱為 gid;
  • 我們可以從 /etc/passwd 文件中看到一個用戶的 uid 和 gid,比如我當前的用戶是 whowin,我可以這樣看到這個用戶的 uid 和 gid

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)4

圖4:當前用戶的uid和gid

  • 圖中黃色框中的兩組數字,前面的 1000 是 uid,後面的 1000 是 gid
  • root 用戶的 uid = 0,gid = 0,這個可是不能亂改的;

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)5

圖5:root用戶的uid和gid

  • 當一個用戶執行一個程序時,需要創建一個進程,Linux 會賦予這個進程一定的權限,當然是賦予執行這個程序的用戶所擁有的權限,比如我用 whowin 這個用戶運行 touch file.tmp,則 touch 進程獲得的是 whowin 的權限,whowin 有在當前目錄下創建文件的權限,這個命令創建了一個新文件 file.tmp 該文件的所有人(owner)是 whowin;

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)6

圖6:whowin用戶執行touch命令

  • 同樣是 whowin 用戶,當執行 cat /etc/shadow 時,權限就不夠了,前面說過 /etc/shadow 的權限是 640,隻有 root 才能讀寫該文件,Linux 把用戶 whowin 的權限賦予 cat 進程,whowin 沒有權限讀取 /etc/shadow,所以 cat 進程無法繼續執行;

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)7

圖7:用戶權限不夠

  • 以上例子隻是要說明,正常情況下 Linux 會把當前用戶的權限賦予其創建的進程,如果這個用戶沒有足夠的權限,則其創建的進程也沒有足夠的權限;
  • 但是,Linux 并不是通過判斷 uid 和 gid 來确定一個進程的權限的,而是通過 euid(effective uid) 和 egid(effective gid) 來決定賦予進程什麼權限,就像我們上面的例子一樣,大多數情況下,uid 和 euid 是相同的,gid 和 egid 是相同的;
  • 但是,這樣的一種機制顯然帶來了一種可能性,即:uid 和 euid 不同;那麼這種不同會帶來什麼呢?當一個 uid=1000 的用戶運行一個程序 A,如果此時 euid=0,那麼程序 A 将獲得 root 的權限

5. setuid、setgid 和 sticky bit

  • setuid、setgid 和 sticky bit 其實都是文件權限的标志位,在詳細介紹這幾個标志位之前,我們需要先回顧一下一般意義上的文件權限;
  • 我們常說一個文件的權限是 0755,其中 7 代表擁有者的權限,中間的 5 代表該文件所在的組的權限,最右邊的 5 代表其它用戶的權限,那麼最前面的 0 是什麼意思呢?記得曾經有人告訴我,這個 0 的意思是指權限是使用八進制表示的,聽上去有點道理,因為表達八進制數字時确實是需要在前面加上一個 0 的,但這裡的這個 0 卻有着更深的含義,而且,這個 0 也可以是大于 0 的值,這些我們後面再說;
  • 文件權限的确是用八進制數表示的,權限分為三組,分别是:擁有者、組和其它用戶,每組的讀、寫、執行權限用一個八進制數表示(三位二進制數);

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)8

圖8:Linux文件權限示意圖

  • 下面我們用一個實例來說明文件權限為 4755 時的不同
  1. 首先我們編一個打開文件的小程序 open_file,并建立一個隻有 root 用戶可以讀取的文件 rootfile.txt

程序 open_file.c 的源代碼

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> int main(int argc, char **argv) { int fd = 0; if (argc != 2) { printf("Usage: %s [filename]\n", argv[0]); return -1; } fd = open(argv[1], O_RDONLY); if (fd == -1) { fprintf(stderr, "%s\n", strerror(errno)); return -1; } close(fd); printf("Open file %s successfully.\n", argv[1]); return 0; }

建立一個隻有 root 可以讀的文件 rootfile.txt

echo "This file's owner is root.">rootfile.txt chmod 600 rootfile.txt sudo chown root:root rootfile.txt ls -l rootfile.txt cat rootfile.txt

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)9

圖9:編譯程序并建立一個隻有root可讀的文件

我們看到當前用戶 whowin 是不能讀取文件 rootfile.txt 的

2. 用程序 open_file 打開 rootfile.txt

./open_file rootfile.txt

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)10

圖10:程序open_file無法打開文件rootfile.txt

權限不夠是因為 Linux 在執行 open_file 程序時将當前用戶 whowin 的權限賦予了 open_file 進程,而 whowin 沒有讀取文件 rootfile.txt 的權限;

3. 将程序 open_file 的所有者改為 root 并再次嘗試打開文件 rootfile.txt

sudo chown root:root open_file ls -l open_file ./open_file rootfile.txt

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)11

圖11:将文件所有者改為root并再次執行

權限還是不夠,盡管 open_file 的擁有者變成了 root,whowin 仍有執行 open_file 的權限,但讀取 rootfile.txt 時仍然使用的是 whowin 的權限,也就是說 Linux 在執行 open_file 時将 whowin 的權限賦予了 open_file 進程,而沒有将這個文件的擁有者 root 的權限賦予 open_file 進程

4. 将 open_file 的權限改為 4755 并再次嘗試打開文件 rootfile.txt

sudo chmod 4755 open_file ls -l open_file ./open_file rootfile.txt

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)12

圖12:将程序的權限改為4755并再次執行

第一個驚喜是,當我們把 open_file 的權限改為 4755 後,ls -l open_file 顯示的權限從 rwx 變成了 rws

第二個驚喜是 open_file 居然可以打開文件 rootfile.txt 了,這說明 open_file 的權限被改為 4755 後,Linux 在執行 open_file 時不像以前一樣把當前用戶 whowin 的權限賦予 open_file 進程,而是把 open_file 的擁有者 root 的權限賦予了 open_file 進程,這才使得 open_file 進程有足夠的權限打開文件 rootfile.txt;

setuid

setuid 是文件權限的一個标志位,當這個标志位置為 1 時,Linux 在執行這個文件時将會把這個文件的所有者的權限賦予這個程序的進程,而不是像普通可執行文件那樣将執行該文件的用戶的權限賦予這個程序的進程;

換句話說,當一個文件設置了 setuid 标志後,用戶執行這個文件時,在這個程序的進程中 uid 為用戶的 uid,euid 為這個文件擁有者的 uid;

  • 隻有這個文件的擁有者有可執行權限時,setuid 才有意義;
  • 設置這個标志可以使用下面這些命令:

chmod u s [文件名]

或者:

chmod 4755 [文件名]

  • 用 ls -l 顯示這個文件時,當該文件的擁有者有可執行權限時,x 将被改為 s;否則,- 将被改為 S;我們用文件 rootfile.txt 看一下

ls -l rootfile.txt sudo chmod u s rootfile.txt ls -l rootfile.txt sudo chmod u-s rootfile.txt ls -l rootfile.txt

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)13

圖13:設置非可執行文件的setuid

  • 前面提到過 sudo 的例子,正是 setuid 的作用使得普通用戶可以通過執行 sudo 進行提權;另一個例子是 passwd 命令,普通用戶可以通過這個命令修改自己的密碼,但這個文件是要讀寫 /etc/shadow 文件的,而隻有 root 才有這個權限,所以執行 passwd 是需要 root 權限的,普通用戶可以正常使用它也是由于這個文件設置了 setuid 标志位;
  • 所以,用戶在執行 sudo 時,sudo 進程中的 euid=0;同樣,用戶在執行 passwd 時,paswd 進程中的 euid=0
setgid

理解了 setuid 之後 setgid 就比較容易理解了,setgid 也是文件權限的一個标志位,當設置該标志位後,用戶執行該文件時,将把該文件所在的組的權限賦予這個程序進程的組權限,而不是使用當前用戶的組權限去執行該程序;

換句話說,當一個文件設置了 setgid 标志後,用戶執行這個文件時,在這個程序的進程中 gid 為用戶的 gid,egid 為這個文件擁有者的 gid;

  • 和 setuid 一樣,隻有這個文件的用戶組有可執行權限時,setgid 才有意義
  • 設置這個标志可以使用下面這些命令:

chmod g s [文件名]

或者:

chmod 2755 [文件名]

  • 用 ls -l 顯示這個文件時,當該文件用戶組有可執行權限時,x 将被改為 s;否則,- 将被改為 S;讀者可以參照 setuid 自己試一下;
sticky bit

sticky bit 是一個比較特殊的東西,這個标志隻有用于目錄時才有效,當一個目錄被設置 sticky bit 後,在這個目錄下的所有文件隻有文件的擁有者和 root 可以删除,不管這個文件的權限是什麼(哪怕是 0777 的權限),所以 sticky bit 又可以被稱為 “限制删除标志

最典型的例子是 /tmp 目錄,每個用戶都可以向這個文件中寫入文件,假定有 whowin 和 demo 用戶都在這個目錄下寫了文件,如果 demo 用戶删除了 whowin 用戶寫入的文件,就有可能出現問題,所以這個目錄被設置了 sticky bit,其中的文件隻有擁有者和 root 可以删除;

設置 sticky bit 可以使用以下命令

chmod t [目錄名]

或者:

chmod 1777 [目錄名]

當一個目錄被設置 sticky bit 後,在其他用戶權限中的可執行位将被改為 t

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)14

圖14:顯示設置sticky bit的目錄

  • setuid、setgid 和 sticky bit 組成了文件權限的另一個八進制數

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)15

圖15:Linux文件權限标志示意圖

  • 這三個标志位又湊夠了一個八進制數,和文件權限的 3 個八進制合在一起組成了 4 個八進制數,文章開頭提到的 sudo 文件權限 4755 中的 4 就是這幾個标志位組成的八進制數;
  • 理論上,這三個标志是可以同時設置的,但這樣設置未必有什麼意義,我們可以用上面曾用到的 sticky 目錄試一下

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)16

圖16:在sticky目錄上同時設置三個标志

  • 盡管我們設置成功了,但是可以想一下 setuid 和 setgid 在這個目錄上的意義何在;
  • 清除标志的命令,前面的例子中已經出現過

chmod u-s [文件名] chmod g-s [文件名] chmod -t [目錄名]

  • chmod 0755 并不一定能清除标志,我們還以 sticky 目錄為例

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)17

圖17:chmod 0755全部無法清除标志

  • chmod 0755 僅清除了 sticky bit;chmod 00755 清除了所有三個标志

6. uid 和 euid 的實驗

  • setuid 之所以能有上面提到的效果,實際上取決于 Linux 在打開一個程序文件創建進程時如何設置 euid,Linux 發現程序文件有 setuid 标志時,則将該文件擁有者的 uid 賦予 euid,否則将當前用戶的 uid 賦予 euid;
  • 下面我們編一個小程序直觀地看一下 uid 和 euid 的變化;
  • 程序 get_euid.c 的源代碼

#include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(void) { uid_t uid; uid_t euid; uid = getuid(); euid = geteuid(); printf("uid = %d\neuid = %d\n", uid, euid); return 0; }

  • 編譯該程序

gcc get_euid.c -o get_euid

  • 将該程序的擁有者變成 root(不設置 setuid),運行一下看看 uid 和 euid 都是什麼

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)18

圖18:沒有設置setuid時的euid

  • 将該程序設置 setuid,再運行一下看看 uid 和 euid 都是什麼

linux存儲管理策略(Linux文件權限setuidsetgid和sticky)19

圖19:設置setuid後的euid

  • 很明顯,當我們設置了 setuid 以後,盡管 uid 還是 1000(whowin 用戶的 uid),但 euid 已經變成了 0(root 的 uid),Linux 會根據 euid 為進程賦予權限;

7. 後記

  • 其實寫這篇文章時,感覺還有好多地方可以展開,但是如果真的展開了就失去重點了,所以隻能收着點,免得文章又臭又長,如果有時間或許今後會專門寫一篇關于 Linux 文件權限和文件屬性的文章;
  • Linux 的文件目錄其實也是一個文件,隻是一般情況下無法像普通文件一樣打開罷了,如果能很容易地打開并讀出,就可以很清楚地看到文件權限在文件目錄中的存儲方式;
  • C 語言中有 getuid()、setuid()、geteuid()、seteuid()等與本文有關的函數,可以自己編程試一下;
  • 希望本文能夠對讀者理解 Linux 的權限管理有所幫助;
  • 歡迎訪問我的博客:https://whowin.gitee.io
,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关科技资讯推荐

热门科技资讯推荐

网友关注

Copyright 2023-2025 - www.tftnews.com All Rights Reserved