linux核心工程導論-Linux使用者和許可權系統
Linux使用者和許可權系統
簡介
我們使用linux系統,一定會使用一個使用者,沒有使用者就不可能使用任何的系統功能,包括系統呼叫。因為系統呼叫本身也是要有使用者的。我們剛登入一個系統,需要一個login程式,驗證了使用者名稱密碼之後,就會返回給你一個shell。後面執行的內容都是在shell中執行的。包括可以在shell中設定ulimit做資源限制,我們也可以發現使用者和密碼都儲存在/etc/passwd和/etc/shadow這些檔案中,很有可能產生使用者許可權校驗是在系統級做的,而不是在核心中做的錯覺。
所有的許可權校驗都是在核心中完成的,但是策略都是在使用者空間配置的。
系統啟動時的許可權
為了最根本的解釋整個過程,我們不使用
系統執行命令除了通過shell啟動還可以通過其他的程序啟動。由於linux核心設計的程序繼承關係,第一個程序是init程序,這個程序是root許可權,擁有最高的完全許可權。如果你直接修改init程式,在裡面實現邏輯,也是具有完全許可權的,核心的所有許可權檢查都可以完全通過。
後來啟動的程序全部是init程序複製出來的(感謝linux的程序啟動方式),linux有兩種複製介面(vfork不算),fork和clone。fork就是傻傻的完全複製,然後使用execve系統呼叫執行新的命令。execve系統呼叫的引數也只是路徑,引數和環境變數,而繼承自父程序的所有許可權和控制代碼都還在。而
啟動後面的低許可權程序和使用者的過程是一個不斷降低許可權的過程。例如不共享開啟的檔案控制代碼,設定新的更小capabilities許可權,使用許可權更小的使用者啟動程式等。設定這種區別的方法是使用高許可權的程序呼叫系統呼叫例如用unshare,prctl或setuid來完成的。這些程序許可權高的原因是使用的root使用者,是這個使用者的許可權高(後面我們會說兩種不同的程序使用者許可權)。
su命令是我們常用的切換到其他使用者執行命令的方法,它所使用的就是
系統啟動後的許可權
系統正常執行之後,程序要麼由其他的程序啟動,要麼由shell啟動。由其他程序啟動的很多也是由shell啟動的。Shell是linux的中堅,Shell程式本身也必須有一個使用者。例如登入使用的使用者user1,那麼login程式(root許可權)驗證了這個使用者名稱密碼後,就可以setuid設定啟動的shell以特定的許可權使用者執行。圖形介面也是使用特定的使用者運行了基礎的組建之後再衍生(例如lightdm)。
Shell之所以重要就是因為它就是中轉的中心。我們完全可以不需要shell,使用圖形介面做中轉中心,也可以使用一個服務程序,接收遠端的socket指令作為中轉中心。Ssh是服務程序作為中轉中心的例子,但是登陸之後中心還是轉變為了shell。
我們也可以發現/etc/passwd中給每個使用者都定義了shell,也可以使用/bin/false,可以看出shell的思想已經固化到系統中了。所以shell不再是一個簡單的應用程式,而是一個系統元件(它本質就是一個應用程式)。
當我們在shell中切換使用者的時候,我們是用新的使用者開啟了新的shell。這個shell的執行者就是新的使用者,執行的命令也自然繼承了新使用者。而我們可以通過su或sudo改變這種情況。Su可以執行在低許可權的shell中,但是切換到高許可權的shell,是因為su命令本身是有suid的,這個二進位制標誌可以讓程序被普通使用者執行,但是卻擁有root的操作許可權。
核心中的使用者和許可權模型
Linux中為了定義許可權,設計了一個物件模型。這個模型主要元件有:object,subject,context,action,rule。
l 常見的物件都是object(Tasks,Files/inodes,Sockets,Message queues,Shared memory segments,Semaphores,Keys)。
l 當object作用在其他object的時候,發起方就是subject,最常見的變成subject的object的是task。
l 無論是object還是subject都有自己的context,context主要是一些歸屬於自身的許可權儲存(credentials)。
l 一個subject作用到另外一個object的動作叫做action
l 限制一個action許可權的叫做rule。Rule包括MAC和DAC兩類,常見的selinux規則就是在這裡工作
這一整個模型運作起來,給所有的物件都賦予了靜態和動態的許可權,就形成了完整的許可權系統。
我們常見的使用者和檔案許可權工作在context層次,linux起了一個名字叫credentials。credentials目前有7種,這些許可權檢查都是要同時滿足的,一個不滿足就不通過安全檢查:
l 傳統的uid系列,設定在檔案object context credentials層次,但是euid系列是設定在subject context credentials層次的。但是都是在檔案本身儲存的,除非不是物理的檔案。
l Capabilities。這是linux獨創的系統。將系統分成了幾個部分,許可權一個一個子系統的設定和刪除。這是程序subject context的內容
l Securebits是在capabilities機制執行比較老,並且很多不適應。
l Keys and keyrings
l Lsm。這是大部分大規模的安全系統所基於的核心安全機制。通過在常用的路徑上安裝鉤子實現,是完全獨立於模型的機制
l AF_KEY。這是網路層的許可權控制,主要用於ipsec
當我們建立一個程序的時候,核心是知道是誰建立的,並且會設定該程序task_struct的對應的域(cred和real_cred),裡面有儲存有效使用者和實際使用者的id。核心知道並不是通過系統呼叫傳遞的,而是程序是複製產生的。就是上一個程序的id。如果上一個程序要用其他的id啟動程序,它會首先setuid改變自己(例如su命令),然後再做系統呼叫的。
所以核心本身也不知道具體使用者的名字和它對應的shell和組。這些都是儲存在使用者可見的檔案系統,由使用者維護。呼叫su命令後面加的使用者名稱也是在使用者空間去查到的對應的uid。登陸時候驗證密碼也是login程式計算驗證直接通過檔案查詢的結果,並不需要經過核心。
核心中並沒有專門的資料庫儲存使用者和組資訊,而是依附在每個程序上的。至於每個程序都可以有多種使用者和組的設定,還包括執行緒的處理(linux核心是基於單個執行緒(程序)的,而執行緒要求程序範圍內統一)。
Linux安全體系
核心只管許可權,其他不管。如果檔案的所有者是普通程序,但是卻呼叫了reboot。只要執行的時候有root許可權,這個程式就能夠執行成功(除非額外設定了系統呼叫限制或capbilities等其他安全維度)。所以核心預設大部分是依賴於是誰(uid)在執行程式的,而不是依賴執行的內容。
而大部分額外的安全系統(例如apparmor,selinux,seccomp,capabilities,acl 等),都是對預設最有效的基於使用者的安全體系的補充,大都作用於執行的內容,而不是執行的使用者,並且作用在不同的位置。
使用者和組概念
使用者和組是獨立於檔案和程序存在的,他們被儲存在/etc/下的幾個檔案中。
一個使用者可以屬於多個組,一個主要組,其它的叫做supplementary group。這是歷史原因,這些組之間沒什麼區別。
檔案object靜態的包含了使用者和組設定,而task(程序)也分為靜態和動態的使用者和組的設定。
檔案object許可權
檔案有標準許可權和擴充套件許可權。標準許可權包括使用者,組,其他組三個的r、w、x、s、t許可權。s是讓非root程式以root身份執行的標誌位,t是讓程序啟動後滯留記憶體不被回收的標誌位。還有該檔案屬於哪個使用者哪個組。
擴充套件的有i、a比較常見(主要是ext系列檔案系統支援),i限制檔案不能被刪除,a限制檔案只能追加,不同的檔案系統可以實現不同的擴充套件許可權,甚至可以使用者自己定義,acl擴充套件是所有檔案系統都有的能力。
設定標準許可權用chmod,設定擴充套件許可權用chattr。
程序的object和subject許可權
程序看起來是動態的,但是他們既是object(靜態的一面)也是subject(動態的一面)。靜態的一面就是我們設定在檔案上的使用者和組以及其對應的許可權,大部分情況下,核心依據這個資訊進行許可權判斷。這個使用者叫做實際使用者。
動態的一面就是當檔案suid置位的時候,使用者以root許可權執行,這時程序的許可權是root的,這個使用者就是有效使用者。理論上有效使用者不一定要是root,但是目前都是。
Sgid也是一個道理。
所以檔案object本身的許可權設定既提供了檔案object本身的許可權,也提供了程序object的許可權和程序subject的許可權。
總結
Linux中預設基於使用者的安全,並且各個檔案系統的許可權位相配合。後續發展了多種維度基於行為的安全機制,成為linux安全系統發展的方向。安全系統大部分都在核心中完成,核心只是一個管理和驗證系統,並不是所有的安全驗證都在核心,系統層次可以完成相當多的許可權驗證。