首頁 > 軟體

Linux能力(capability)機制的繼承

2020-06-16 17:44:45

1、Linux能力機制概述

在以往的UNIX系統上,為了做進程的許可權檢查,把進程分為兩類:特權進程(有效使用者ID是0)和非特權進程(有效使用者ID是非0)。特權進程可以通過核心所有的許可權檢查,而非特權進程的檢查則是基於進程的身份(有效ID,有效組及補充組資訊)進行。

從linux核心2.2開始,Linux把超級使用者不同單元的許可權分開,可以單獨的開啟和禁止,稱為能力(capability)。可以將能力賦給普通的進程,使其可以做root使用者可以做的事情。

此時核心在檢查進程是否具有某項許可權的時候,不再檢查該進程的是特權進程還是非特權進程,而是檢查該進程是否具有其進行該操作的能力。例如當進程設定系統時間,核心會檢查該進程是否有設定系統時間(CAP_SYS_TIME)的能力,而不是檢查進程的ID是否為0;

當前Linux系統中共有37項特權,可在/usr/include/linux/capability.h檔案中檢視

2、Linux能力機制的實現

一個完整的能力機制需要滿足以下三個條件:

1、對進程的所有特權操作,linux核心必須檢查該進程該操作的特權位是否使能。

2、Linux核心必須提供系統呼叫,允許進程能力的修改與恢復。

3、檔案系統必須支援能力機制可以附加到一個可執行檔案上,但檔案執行時,將其能力附加到進程當中。

到linux核心版本2.6.24為止,上述條件的1、2可以滿足。從linux核心2.6.24開始,上述3個條件可以都可以滿足

每個進程包括三個能力集,含義如下:

Permitted: 它是effective capabilities和Inheritable capability的超集。如果一個進程在Permitted集合中丟失一個能力,它無論如何不能再次獲取該能力(除非特權使用者再次賦予它)

Inheritable: 它是表明該進程可以通過execve繼承給新進程的能力。

Effecitive: Linux核心真正檢查的能力集。

從2.6.24開始,Linux核心可以給可執行檔案賦予能力,可執行檔案的三個能力集含義如下:

Permitted:該能力當可執行檔案執行時自動附加到進程中,忽略Inhertiable capability。

Inheritable:它與進程的Inheritable集合做與操作,決定執行execve後新進程的Permitted集合。

Effective: 檔案的Effective不是一個集合,而是一個單獨的位,用來決定進程成的Effective集合。

有上述描述可知,Linux系統中的能力分為兩部分,一部分是進程能力,一部分是檔案能力,而Linux核心最終檢查的是進程能力中的Effective。而檔案能力和進程能力中的其他部分用來完整能力繼承、限制等方面的內容。

3、Linux能力機制的繼承

在linux終端檢視capabilities的man手冊,其中有繼承關係公式如下


複製程式碼
      P'(permitted) = (P(inheritable) & F(inheritable)) |
                          (F(permitted) & cap_bset)              //新進程的permitted有老進程的和新進程的inheritable和可執行檔案的permitted及cap_bset運算得到.
      P'(effective) = F(effective) ? P'(permitted) : 0            //新進程的effective依賴可執行檔案的effective位,使能:和新進程的permitted一樣,負責為空
      P'(inheritable) = P(inheritable)    [i.e., unchanged]      //新進程的inheritable直接繼承老進程的Inheritable

      說明:

      P  在執行execve函數前,進程的能力
      P'  在執行execve函數後,進程的能力
      F  可執行檔案的能力
      cap_bset 系統能力的邊界值,在此處預設全為1

有測試程式如下:

father.c

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/capability.h>
#include <errno.h>

void list_capability()
{
    struct __user_cap_header_struct cap_header_data;
    cap_user_header_t cap_header = &cap_header_data;

    struct __user_cap_data_struct cap_data_data;
    cap_user_data_t cap_data = &cap_data_data;

    cap_header->pid = getpid();
    cap_header->version = _LINUX_CAPABILITY_VERSION_1;

    if (capget(cap_header, cap_data) < 0) {
        perror("Failed capget");
        exit(1);
    } 
    printf("Cap data permitted: 0x%x,  effective: 0x%x,  inheritable:0x%xn",
        cap_data->permitted, cap_data->effective,cap_data->inheritable);
}

int main(void)
{
    cap_t caps = cap_init();
    cap_value_t capList[2] = {CAP_DAC_OVERRIDE, CAP_SYS_TIME};
    unsigned num_caps = 2;
    //cap_set_flag(caps, CAP_EFFECTIVE, num_caps, capList, CAP_SET);
    cap_set_flag(caps, CAP_INHERITABLE, num_caps, capList, CAP_SET);
    cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);

    if(cap_set_proc(caps))
    { 
        perror("cap_set_proc");
    } 

    list_capability();

    execl("/home/xlzh/code/capability/child", NULL);

    sleep(1000);
}

child.c

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/capability.h>
#include <errno.h>

void list_capability()
{
    struct __user_cap_header_struct cap_header_data;
    cap_user_header_t cap_header = &cap_header_data;

    struct __user_cap_data_struct cap_data_data;
    cap_user_data_t cap_data = &cap_data_data;

    cap_header->pid = getpid();
    cap_header->version = _LINUX_CAPABILITY_VERSION_1;

    if (capget(cap_header, cap_data) < 0) {
        perror("Failed capget");
        exit(1);
    }
    printf("child Cap data permitted: 0x%x, effective: 0x%x, inheritable:0x%xn", cap_data->permitted, cap_data->effective,cap_data->inheritable);
}

int main(void)
{
    list_capability();
    sleep(1000);
}

執行結果分析

linuxidc@linuxidc:~/code/capability$ gcc child.c -o child
linuxidc@linuxidc:~/code/capability$ gcc father.c -o father -lcap
linuxidc@linuxidc:~/code/capability$ sudo setcap cap_dac_override,cap_sys_time+ei child
linuxidc@linuxidc:~/code/capability$ sudo setcap cap_dac_override,cap_sys_time+ip father
 /* 單獨執行,child檔案有E(effective)I(inheritable)的能力,執行child的終端沒有任何能力, 套用公式(cap_bset預設全1)
  * P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & cap_bset)  // P'(permitted) = (0x0 & 0x2000002) | (0x0 & 全1),結果為0
  * P'(effective) = F(effective) ? P'(permitted) : 0                              // P'(effective) = 1 ? P'(permitted) : 0, 結果為P'(permitted),即0
  * P'(inheritable) = P(inheritable)                                              // P'(inheritable) = 0
  * 執行結果如下所示
  */
linuxidc@linuxidc:~/code/capability$ ./child
child Cap data permitted: 0x0, effective: 0x0, inheritable 0x0
/* 單獨執行,child檔案有E(effective)I(inheritable)的能力,執行child的father檔案有E(inheritable)和P(permitted)能力, 套用公式
  * P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & cap_bset)  // P'(permitted) = (0x2000002 & 0x2000002) | (0x2000002 & 全1),結果為0
  * P'(effective) = F(effective) ? P'(permitted) : 0                              // P'(effective) = 1 ? P'(permitted) : 0, 結果為P'(permitted),即0x2000002
  * P'(inheritable) = P(inheritable)                                              // P'(inheritable) = 0x2000002
  * 執行結果如下所示
  */
linuxidc@linuxidc:~/code/capability$ ./father
father Cap data permitted: 0x2000002, effective: 0x0, inheritable: 0x2000002
child Cap data permitted: 0x2000002, effective: 0x2000002, inheritable 0x2000002

上述單獨執行child可執行程式,其進程沒有任何能力。但是有father進程來啟動執行child可執行程式,其進程則有相應的能力。

上例中father和child的能力都設定的cap_dac_override和cap_sys_time兩個能力。其實兩個可執行程式設定的能力可以不同,各位讀者可以自己修改其能力,套用公式進行計算。

4、以root使用者身份執行程式

1、以root使用者身份執行程式,則該進程所有能力的的P和I都置為1

2、以root使用者身份執行程式,則該進程的E使能

/*由於執行child的終端進程沒有I能力,所有child進程的inheritable也為0, 其他能力為全1*/
linuxidc@linuxidc:~/code/capability$ sudo ./child
child Cap data permitted: 0xffffffff, effective: 0xffffffff, inheritable 0x0

5、進程使用者ID的變化對能力的影響

1、當一個進程的有效使用者ID從0變化到非0, 那麼所有的E能力清零

2、當一個進程的有效使用者ID從非0變化到0,那麼現有的P集合拷貝到E集合

3、如果一個進行原來的真實使用者ID,有效使用者ID,儲存設定使用者ID是0,由於某些操作這些ID都變成了非0,那麼所有的的P和E能力全部清理

4、如果一個檔案系統的使用者ID從0變成非0,那麼以下的能力在E集合中清除:CAP_CHOWN, CAP_DAC_OVERRIDE,  CAP_DAC_READ_SEARCH,  CAP_FOWNER,  CAP_FSETID,  CAP_LINUX_IMMUTABLE  (since  Linux  2.2.30),  CAP_MAC_OVERRIDE,  CAP_MKNOD,如果一個檔案系統的使用者ID從0變成非0,那麼在P集合中使能的能力將設定到E集合中。

本文永久更新連結地址http://www.linuxidc.com/Linux/2016-03/129561.htm


IT145.com E-mail:sddin#qq.com