linux setcap命令詳解(包括各個cap的使用舉例)【轉】


轉自:https://blog.csdn.net/megan_free/article/details/100357702

轉載自:https://www.cnblogs.com/iamfy/archive/2012/09/20/2694977.html

Linux的capability深入分析     

這個也是轉載頁面,原始頁面沒找到,謝謝原作者!

一)概述:

1)從2.1版開始,Linux內核有了能力(capability)的概念,即它打破了UNIX/LINUX操作系統中超級用戶/普通用戶的概念,由普通用戶也可以做只有超級用戶可以完成的工作.

Capabilities的主要思想在於分割root用戶的特權,即將root的特權分割成不同的能力,每種能力代表一定的特權操作。例如:能力CAP_SYS_MODULE表示用戶能夠加載(或卸載)內核模塊的特權操作,而CAP_SETUID表示用戶能夠修改進程用戶身份的特權操作。在Capbilities中系統將根據進程擁有的能力來進行特權操作的訪問控制。
    在Capilities中,只有進程和可執行文件才具有能力,每個進程擁有三組能力集,分別稱為cap_effective, cap_inheritable, cap_permitted(分別簡記為:pE,pI,pP),其中cap_permitted表示進程所擁有的最大能力集;cap_effective表示進程當前可用的能力集,可以看做是cap_permitted的一個子集;而cap_inheitable則表示進程可以傳遞給其子進程的能力集。系統根據進程的cap_effective能力集進行訪問控制,cap_effective為cap_permitted的子集,進程可以通過取消cap_effective中的某些能力來放棄進程的一些特權。可執行文件也擁有三組能力集,對應於進程的三組能力集,分別稱為cap_effective, cap_allowed 和 cap_forced(分別簡記為fE,fI,fP),其中,cap_allowed表示程序運行時可從原進程的cap_inheritable中集成的能力集,cap_forced表示運行文件時必須擁有才能完成其服務的能力集;而cap_effective則表示文件開始運行時可以使用的能力。

各種能力:
CAP_CHOWN:修改文件屬主的權限
CAP_DAC_OVERRIDE:忽略文件的DAC訪問限制
CAP_DAC_READ_SEARCH:忽略文件讀及目錄搜索的DAC訪問限制
CAP_FOWNER:忽略文件屬主ID必須和進程用戶ID相匹配的限制
CAP_FSETID:允許設置文件的setuid位
CAP_KILL:允許對不屬於自己的進程發送信號
CAP_SETGID:允許改變進程的組ID
CAP_SETUID:允許改變進程的用戶ID
CAP_SETPCAP:允許向其他進程轉移能力以及刪除其他進程的能力
CAP_LINUX_IMMUTABLE:允許修改文件的IMMUTABLE和APPEND屬性標志
CAP_NET_BIND_SERVICE:允許綁定到小於1024的端口
CAP_NET_BROADCAST:允許網絡廣播和多播訪問
CAP_NET_ADMIN:允許執行網絡管理任務
CAP_NET_RAW:允許使用原始套接字
CAP_IPC_LOCK:允許鎖定共享內存片段
CAP_IPC_OWNER:忽略IPC所有權檢查
CAP_SYS_MODULE:允許插入和刪除內核模塊
CAP_SYS_RAWIO:允許直接訪問/devport,/dev/mem,/dev/kmem及原始塊設備
CAP_SYS_CHROOT:允許使用chroot()系統調用
CAP_SYS_PTRACE:允許跟蹤任何進程
CAP_SYS_PACCT:允許執行進程的BSD式審計
CAP_SYS_ADMIN:允許執行系統管理任務,如加載或卸載文件系統、設置磁盤配額等
CAP_SYS_BOOT:允許重新啟動系統
CAP_SYS_NICE:允許提升優先級及設置其他進程的優先級
CAP_SYS_RESOURCE:忽略資源限制
CAP_SYS_TIME:允許改變系統時鍾
CAP_SYS_TTY_CONFIG:允許配置TTY設備
CAP_MKNOD:允許使用mknod()系統調用
CAP_LEASE:允許修改文件鎖的FL_LEASE標志

cap_chown=eip是將chown的能力以cap_effective(e),cap_inheritable(i),cap_permitted(p)三種位圖的方式授權給相關的程序文件.

2)capability可以作用在進程上(受限),也可以作用在程序文件上,它與sudo不同,sudo只針對用戶/程序/文件的概述,即sudo可以配置某個用戶可以執行某個命令,可以更改某個文件,而capability是讓某個程序擁有某種能力,例如:

capability讓/tmp/testkill程序可以kill掉其它進程,但它不能mount設備節點到目錄,也不能重啟系統,因為我們只指定了它kill的能力,即使程序有問題也不會超出能力范圍.

3)每個進程有三個和能力有關的位圖:inheritable(I),permitted(P)和effective(E),對應進程描述符task_struct(include/linux/sched.h)里面的cap_effective, cap_inheritable, cap_permitted,所以我們可以查看/proc/PID/status來查看進程的能力.

4)cap_effective:當一個進程要進行某個特權操作時,操作系統會檢查cap_effective的對應位是否有效,而不再是檢查進程的有效UID是否為0.

例如,如果一個進程要設置系統的時鍾,Linux的內核就會檢查cap_effective的CAP_SYS_TIME位(第25位)是否有效.

5)cap_permitted:表示進程能夠使用的能力,在cap_permitted中可以包含cap_effective中沒有的能力,這些能力是被進程自己臨時放棄的,也可以說cap_effective是cap_permitted的一個子集.

6)cap_inheritable:表示能夠被當前進程執行的程序繼承的能力.

二)capability的設定與清除

我們在下面的程序中給當前的進程設定能力,最后我們清除掉所設定的能力,源程序如下:

復制代碼

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <sys/types.h>
 5 #include <unistd.h>
 6  
 7 #undef _POSIX_SOURCE
 8 #include <sys/capability.h>
 9  
10 extern int errno;
11   
12 void whoami(void)
13 {
14   printf("uid=%i  euid=%i  gid=%i\n", getuid(), geteuid(), getgid());
15 }
16  
17 void listCaps()
18 {
19   cap_t caps = cap_get_proc();
20   ssize_t y = 0;
21   printf("The process %d was give capabilities %s\n",
22          (int) getpid(), cap_to_text(caps, &y));
23   fflush(0);
24   cap_free(caps);
25 }
26   
27 int main(int argc, char **argv)
28 {
29   int stat;
30   whoami();
31   stat = setuid(geteuid());
32   pid_t parentPid = getpid();
33  
34   if(!parentPid)
35     return 1;
36   cap_t caps = cap_init();
37  
38  
39   cap_value_t capList[5] =
40   { CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP } ;
41   unsigned num_caps = 5;
42   cap_set_flag(caps, CAP_EFFECTIVE, num_caps, capList, CAP_SET);
43   cap_set_flag(caps, CAP_INHERITABLE, num_caps, capList, CAP_SET);
44   cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);
45  
46   if (cap_set_proc(caps)) {
47     perror("capset()");
48  
49     return EXIT_FAILURE;
50   }
51   listCaps();
52 
53   printf("dropping caps\n");
54   cap_clear(caps);  // resetting caps storage
55  
56   if (cap_set_proc(caps)) {
57     perror("capset()");
58     return EXIT_FAILURE;
59   }
60   listCaps();
61 
62   cap_free(caps);
63   return 0;
64 
65 }

復制代碼

編譯:

gcc capsettest.c -o capsettest -lcap

運行:

./capsettest 

uid=0  euid=0  gid=0

The process 2383 was give capabilities = cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw+eip

dropping caps

The process 2383 was give capabilities =

注:

1)我們對該進程增加了5種能力,隨后又清除了所有能力.

2)首先通過cap_init()初始化存放cap能力值的狀態,隨后通過cap_set_flag函數的調用,將三種位圖的能力設置給了變量caps,再通過cap_set_proc(caps)設定當前進程的能力值,通過cap_get_proc()返回當前進程的能力值,最后通過cap_free(caps)釋放能力值.

3)cap_set_flag函數的原型是:

int cap_set_flag(cap_t cap_p, cap_flag_t flag, int ncap,const cap_value_t *caps, cap_flag_value_t value);

我們這里的調用語句是:cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);

第一個參數cap_p是存放能力值的變量,是被設定值.這里是caps.

第二個參數flag是是三種能力位圖,這里是CAP_PERMITTED.

第三個參數ncap是要設定能力的個數,這里是num_caps,也就是5.

第四個參數*caps是要設定的能力值,這里是capList數組,也就是CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP.

第五個參數value是決定要設定還是清除,這里是CAP_SET.

4)cap_set_proc函數的原型是:int cap_set_proc(cap_t cap_p);

cap_set_proc函數通過cap_p中的能力值設定給當前的進程.

5)cap_get_proc函數的原型是:cap_t cap_get_proc(void);

cap_get_proc函數返回當前進程的能力值給cap變量.

6)cap_free函數的原型是:cap_free(caps);

cap_free函數清理/釋放cap變量.

7)如果我們fork()了子進程,那么子進程繼承父進程的所有能力.

8)不能單獨設定CAP_EFFECTIVE,CAP_INHERITABLE位圖,必須要和CAP_PERMITTED聯用,且CAP_PERMITTED一定要是其它兩個位圖的超集.

9)如果兩次調用cap_set_proc函數,第二次調用的值力值不能少於或多於第一次調用.如第一次我們授權chown,setuid能力,第二次只能是chown,setuid不能是其它的能力值.

10)普通用戶不能給進程設定能力.

三)進程的能力掩碼:

我們可以通過下面的程序獲取當前進程的掩碼,它是通過capget函數來獲取指定進程的能力掩碼,當然我們也可以用capset來設定掩碼,下面獲取掩碼的體現:

復制代碼

 1 #undef _POSIX_SOURCE
 2 #include <stdlib.h>
 3 #include <stdio.h>
 4 #include <sys/types.h>
 5 #include <unistd.h>
 6 #include <linux/capability.h>
 7 #include <errno.h>
 8 
 9 int main()
10 {
11      struct __user_cap_header_struct cap_header_data;
12      cap_user_header_t cap_header = &cap_header_data;
13 
14      struct __user_cap_data_struct cap_data_data;
15      cap_user_data_t cap_data = &cap_data_data;
16 
17      cap_header->pid = getpid();
18      cap_header->version = _LINUX_CAPABILITY_VERSION_1;
19 
20      if (capget(cap_header, cap_data) < 0) {
21          perror("Failed capget");
22          exit(1);
23      }
24      printf("Cap data 0x%x, 0x%x, 0x%x\n", cap_data->effective,
25          cap_data->permitted, cap_data->inheritable);
26 }

復制代碼

gcc capget0.c -o capget0 -lcap

普通用戶:

./capget0 

Cap data 0x0, 0x0, 0x0

超級用戶:

/home/test/capget0 

Cap data 0xffffffff, 0xffffffff, 0x0

這也說明了默認情況下,root運行的進程是什么權限都有,而普通用戶則什么權限都沒有.

我們可以將本程序與上面的程序進行整合,如下:

復制代碼

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <sys/types.h>
 5 #include <unistd.h>
 6  
 7 #undef _POSIX_SOURCE
 8 #include <sys/capability.h>
 9  
10 extern int errno;
11   
12 void whoami(void)
13 {
14   printf("uid=%i  euid=%i  gid=%i\n", getuid(), geteuid(), getgid());
15 }
16  
17 void listCaps()
18 {
19   cap_t caps = cap_get_proc();
20   ssize_t y = 0;
21   printf("The process %d was give capabilities %s\n",
22          (int) getpid(), cap_to_text(caps, &y));
23   fflush(0);
24   cap_free(caps);
25 }
26  
27  
28 int main(int argc, char **argv)
29 {
30   int stat;
31   whoami();
32   stat = setuid(geteuid());
33   pid_t parentPid = getpid();
34  
35   if(!parentPid)
36     return 1;
37   cap_t caps = cap_init();
38  
39   cap_value_t capList[5] =
40   { CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP } ;
41   unsigned num_caps = 5;
42   cap_set_flag(caps, CAP_EFFECTIVE, num_caps, capList, CAP_SET);
43   cap_set_flag(caps, CAP_INHERITABLE, num_caps, capList, CAP_SET);
44   cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);
45  
46   if (cap_set_proc(caps)) {
47     perror("capset()");
48  
49     return EXIT_FAILURE;
50   }
51   listCaps();
52 
53   cap_free(caps);
54 
55   struct __user_cap_header_struct cap_header_data;
56   cap_user_header_t cap_header = &cap_header_data;
57 
58   struct __user_cap_data_struct cap_data_data;
59   cap_user_data_t cap_data = &cap_data_data;
60 
61   cap_header->pid = getpid();
62   cap_header->version = _LINUX_CAPABILITY_VERSION_1;
63 
64   if (capget(cap_header, cap_data) < 0) {
65       perror("Failed capget");
66       exit(1);
67   }
68   printf("Cap data 0x%x, 0x%x, 0x%x\n", cap_data->effective,
69   cap_data->permitted, cap_data->inheritable);
70 
71   sleep(60);
72   return 0;
73 }

復制代碼

 

編譯並執行:

gcc capsettest.c -o capsettest -lcap

./capsettest

uid=0  euid=0  gid=0

The process 3101 was give capabilities = cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw+eip

Cap data 0x25c0, 0x25c0, 0x25c0

注:0x25c0=10 0101 1100 0000(二進制)

對映的能力如下:

cap_setgid=6(位)

cap_setuid=7(位)

cap_setpcap=8(位)

cap_net_bind_service=10(位)

cap_net_raw=13(位)

在程序sleep的時候,我們查看一下進程的status,如下:

cat /proc/`pgrep capsettest`/status

CapInh: 00000000000025c0

CapPrm: 00000000000025c0

CapEff: 00000000000025c0

CapBnd: ffffffffffffffff

我們看到進程的status也反映了它的能力狀態.

CapBnd是系統的邊界能力,我們無法改變它.

 

四)capability的工具介紹

在我們的試驗環境是RHEL6,libcap-2.16軟件包中包含了相關的capability設置及查看工作,如下:

rpm -ql libcap-2.16-5.2.el6.i686 

/lib/libcap.so.2

/lib/libcap.so.2.16

/lib/security/pam_cap.so

/usr/sbin/capsh

/usr/sbin/getcap

/usr/sbin/getpcaps

/usr/sbin/setcap

/usr/share/doc/libcap-2.16

/usr/share/doc/libcap-2.16/License

/usr/share/doc/libcap-2.16/capability.notes

/usr/share/man/man8/getcap.8.gz

/usr/share/man/man8/setcap.8.gz

getcap可以獲得程序文件所具有的能力(CAP).

getpcaps可以獲得進程所具有的能力(CAP).

setcap可以設置程序文件的能力(CAP).

我們下面主要用setcap來進行調試.

五)CAP_CHOWN 0(允許改變文件的所有權)

授權普通用戶可以用/bin/chown程序更改任意文件的owner,如下:

setcap cap_chown=eip /bin/chown 

查看/bin/chown程序的能力值,如下:

getcap /bin/chown               

/bin/chown = cap_chown+eip

切換到test用戶,將/bin/ls程序的owner改為test,如下:

su - test

chown test.test /bin/ls

ls -l /bin/ls

-rwxr-xr-x. 1 test test 118736 Jun 14  2010 /bin/ls

注:

1)cap_chown=eip是將chown的能力以cap_effective(e),cap_inheritable(i),cap_permitted(p)三種位圖的方式授權給相關的程序文件.

2)如果改變文件名,則能力保留到新文件.

3)用setcap -r /bin/chown可以刪除掉文件的能力.

4)重新用setcap授權將覆蓋之前的能力. 

六)CAP_DAC_OVERRIDE 1(忽略對文件的所有DAC訪問限制)

授權普通用戶可以用/usr/bin/vim程序修改所有文件的內容,如下:

setcap cap_dac_override=eip /usr/bin/vim 

切換到普通用戶

su - test

修改/etc/shadow文件內容

vim /etc/shadow

root:$6$3hJf.BoIVU/cdLKb$JxLXcQScrLS032aFPAQvVc4RzKYNadcIIzxmzAIw.jejrYOHhqdr0oV7sNBL.IhGBo.mMOYEdevlnCp2OGku8.:15094:0:99999:7:::

bin:*:14790:0:99999:7:::

daemon:*:14790:0:99999:7:::

adm:*:14790:0:99999:7:::

注:

DAC_OVERRIDE能力是DAC_READ_SEARCH能力的超集.

 

七)CAP_DAC_READ_SEARCH 2(忽略所有對讀、搜索操作的限制)

授權普通用戶可以用/bin/cat程序查看所有文件的內容,如下:

setcap cap_dac_read_search=eip /bin/cat

切換到普通用戶

su - test

查看/etc/shadow,如下:

cat /etc/shadow

root:$6$3hJf.BoIVU/cdLKb$JxLXcQScrLS032aFPAQvVc4RzKYNadcIIzxmzAIw.jejrYOHhqdr0oV7sNBL.IhGBo.mMOYEdevlnCp2OGku8.:15094:0:99999:7:::

bin:*:14790:0:99999:7:::

daemon:*:14790:0:99999:7:::

adm:*:14790:0:99999:7:::

八)CAP_FOWNER 3(以最后操作的UID,覆蓋文件的先前的UID)

cp /etc/passwd /tmp/

ls -l /tmp/passwd   

-rw-r--r-- 1 root root 1171 2011-04-29 19:21 /tmp/passwd

授權cap_fowner權限給/usr/bin/vim

setcap cap_fowner=eip /usr/bin/vim

切換到test用戶

su - test

編輯/tmp/passwd文件,存盤退出.

vi /tmp/passwd

修改文件,並保存退出.

查看/tmp/passwd,發現owner已經變成test

-rw-r--r-- 1 test test 1176 2011-04-29 19:21 /tmp/passwd

九)CAP_FSETID 4(確保在文件被修改后不修改setuid/setgid位)

起因是當文件被修改后,會清除掉文件的setuid/setgid位,而設定CAP_FSETID后將保證setuid/setgid位不被清除.但這對chown函數無用.

測試程序如下:

復制代碼

 1 #include <sys/types.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <sys/stat.h>
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 #include <string.h>
 9 main()
10 {
11         int handle;
12         char string[40];
13         int length, res;
14 
15         if ((handle = open("/tmp/passwd", O_WRONLY | O_CREAT | O_TRUNC,S_IREAD | S_IWRITE)) == -1)
16         {
17                 printf("Error opening file.\n");
18                 exit(1);
19         }
20         strcpy(string, "Hello, world!\n");
21         length = strlen(string);
22         if ((res = write(handle, string, length)) != length)
23         {
24                 printf("Error writing to the file.\n");
25                 exit(1);
26         }
27         printf("Wrote %d bytes to the file.\n", res);
28         close(handle);
29 }

復制代碼

 

gcc fsetid.c -o fsetid

先測試沒有設FSETID的情況,如下:

chmod 6777 /tmp/passwd

ls -l /tmp/passwd

-rwsrwsrwx 1 test test 14 2011-04-30 14:22 /tmp/passwd

/tmp/fsetid 

Wrote 14 bytes to the file.

ls -l /tmp/passwd

-rwxrwxrwx 1 test test 14 2011-04-30 14:25 /tmp/passwd

我們看到setuid/setgid位被清除了.

下面是設定FSETID,如下:

chmod 6777 /tmp/passwd

ls -l /tmp/passwd     

-rwsrwsrwx 1 test test 14 2011-04-30 14:25 /tmp/passwd

切換到root用戶,給/tmp/fsetid程序授權CAP_FSETID能力,如下:

setcap cap_fsetid=eip /tmp/fsetid

切換到普通用戶

/tmp/fsetid

Wrote 14 bytes to the file.

ls -l /tmp/passwd

-rwsrwsrwx 1 test test 14 2011-04-30 14:28 /tmp/passwd

十)CAP_KILL 5 (允許對不屬於自己的進程發送信號)

我們先模擬沒有加CAP_KILL能力的情況,如下:

終端1,用root用戶啟用top程序,如下:

su - root

top

終端2,用test用戶kill之前的top進程,如下:

pgrep top     

3114

/bin/kill 3114

kill: Operation not permitted

我們發現無法對不屬於自己的進程發送信號.

下面我們用CAP_KILL能力的程序向不屬於自己的進程發送信號,如下:

設定kill命令的kill位,如下:

setcap cap_kill=eip /bin/kill 

殺掉3114進程,沒有問題,如下:

/bin/kill 3114

echo $?

0

注意:

普通用戶要用/bin/kill這種絕對路徑的方式,而不能用kill這種方式.

十一)CAP_SETGID 6 (設定程序允許普通用戶使用setgid函數,這與文件的setgid權限位無關)

cp /etc/shadow /tmp/

chown root.root /tmp/shadow

chmod 640 /tmp/shadow

切換到普通用戶test,並編寫setgid測試程序,如下:

su - test

復制代碼

1 #include <unistd.h>
2 int
3 main ()
4 {
5         gid_t gid = 0;
6         setgid(gid);
7         system("/bin/cat /tmp/shadow");
8         return 0;
9 }

復制代碼

gcc setgid.c -o setgid

更改setgid程序為CAP_SETGID

setcap cap_setgid=eip /tmp/setgid

切換到普通用戶,運行/tmp/setgid程序,如下:

su - test

/tmp/setgid 

root:$1$S9AmPHY8$ZIdORp6aLnYleb5EORxw8/:14479:0:99999:7:::

daemon:*:14479:0:99999:7:::

bin:*:14479:0:99999:7:::

sys:*:14479:0:99999:7:::

sync:*:14479:0:99999:7:::

games:*:14479:0:99999:7:::

man:*:14479:0:99999:7:::

lp:*:14479:0:99999:7:::

mail:*:14479:0:99999:7:::

news:*:14479:0:99999:7:::

uucp:*:14479:0:99999:7:::

proxy:*:14479:0:99999:7:::

www-data:*:14479:0:99999:7:::

backup:*:14479:0:99999:7:::

list:*:14479:0:99999:7:::

我們看到普通用戶可以查看/tmp/shadow文件,而取消CAP_SETGID則使程序不能擁有setgid的權限,如下:

setcap -r /tmp/setgid

su - test

/tmp/setgid 

/bin/cat: /tmp/shadow: Permission denied

十二)CAP_SETUID 7 (設定程序允許普通用戶使用setuid函數,這也文件的setuid權限位無關)

cp /etc/shadow /tmp/

chown root.root /tmp/shadow

chmod 640 /tmp/shadow

切換到普通用戶test,並編寫setuid測試程序,如下:

su - test

cd /tmp/

vi setuid.c

復制代碼

1 #include <unistd.h>
2 int
3 main ()
4 {
5         uid_t uid = 0;
6         setuid(uid);
7         system("/bin/cat /tmp/shadow");
8         return 0;
9 }

復制代碼

gcc setuid.c -o setuid

切換到root用戶,更改setuid程序為CAP_SETUID

su - root

setcap cap_setuid=eip /tmp/setuid

切換到test用戶,運行/tmp/setuid程序,如下:

su - test

/tmp/setuid

root:$1$S9AmPHY8$ZIdORp6aLnYleb5EORxw8/:14479:0:99999:7:::

daemon:*:14479:0:99999:7:::

bin:*:14479:0:99999:7:::

sys:*:14479:0:99999:7:::

sync:*:14479:0:99999:7:::

games:*:14479:0:99999:7:::

man:*:14479:0:99999:7:::

lp:*:14479:0:99999:7:::

我們看到普通用戶可以查看/tmp/shadow文件,而取消CAP_SETUID則使程序不能擁有setuid的權限,如下:

setcap -r /tmp/setuid

su - test

/tmp/setuid 

/bin/cat: /tmp/shadow: Permission denied

十三)CAP_SETPCAP 8 (允許向其它進程轉移能力以及刪除其它進程的任意能力)

事實上只有init進程可以設定其它進程的能力,而其它程序無權對進程授權,root用戶也不能對其它進程的能力進行修改,只能對當前進程通過cap_set_proc等函數進行修改,而子進程也會繼承這種能力.

所以即使使用了CAP_SETPCAP能力,也不會起到真正的作用.

十四)CAP_LINUX_IMMUTABLE 9 (允許修改文件的不可修改(IMMUTABLE)和只添加(APPEND-ONLY)屬性)

普通用戶不能通過chattr對文件設置IMMUTABLE(chattr +i)和APPEND-ONLY(chattr +a)權限,而通過CAP_LINUX_IMMUTABLE可以使普通用戶通過自己增減(immutable/append-only)權限.

普通用戶通過chattr給文件增加immutable權限,如下:

touch /tmp/test

chattr +i /tmp/test

chattr: Operation not permitted while setting flags on /tmp/test

我們看到授權失敗了,而如果我們對chattr增加了LINUX_IMMUTABLE權限,則可以成功,如下:

此時切換到root用戶:

su - 

setcap cap_linux_immutable=eip /usr/bin/chattr 

切換到普通用戶:

su - test

chattr +i /tmp/test

lsattr /tmp/test 

----i-------------- /tmp/test

我們看到授權成功了,注意,這里只能對自己的文件授權(immutable/append-only)權限,對於其它用戶的權限LINUX_IMMUTABLE不起作用(root除外),如下:

su - test

chattr +i /etc/passwd

chattr: Permission denied while setting flags on /etc/passwd

十五)CAP_NET_BIND_SERVICE 10(允許綁定到小於1024的端口)

普通用戶不能通過bind函數綁定小於1024的端口,而root用戶可以做到,CAP_NET_BIND_SERVICE的作用就是讓普通用戶也可以綁端口到1024以下.

普通用戶通過nc綁定端口500,如下:

nc -l -p 500

Can't grab 0.0.0.0:500 with bind : Permission denied

增加CAP_NET_BIND_SERVICE能力到nc程序,如下:

setcap cap_net_bind_service=eip /usr/bin/nc

再切換到普通用戶,通過nc綁定端口500,如下:

nc -l -p 500

查看該端口:

netstat -tulnp|grep nc

tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      2523/nc       

十六)CAP_NET_BROADCAST 11(允許網絡廣播和多播訪問)

事實上它並沒有被應用,普通用戶也可以使用ping -b 192.168.0.255也發送廣播包

十七)CAP_NET_ADMIN 12(允許執行網絡管理任務:接口,防火牆和路由等)

普通用戶不能創建新的網絡接口(interface),不能更改ip地址,而CAP_NET_ADMIN可以幫助普通用戶完成這項工作,如下:

用普通用戶創建新的網卡接口eth0:1失敗

/sbin/ifconfig eth0:1 172.16.27.133 netmask 255.255.255.0

SIOCSIFADDR: Permission denied

SIOCSIFFLAGS: Permission denied

SIOCSIFNETMASK: Permission denied

此時我們把CAP_NET_ADMIN能力授權給ifconfig程序,如下:

setcap cap_net_admin=eip /sbin/ifconfig

我們再次用普通用戶即可以新建網絡接口eth0:1,並可以DOWN掉接口,如下:

/sbin/ifconfig eth0:1 172.16.27.133 netmask 255.255.255.0

/sbin/ifconfig eth0:1

eth0:1    Link encap:Ethernet  HWaddr 00:0c:29:f9:5e:06  

          inet addr:172.16.27.133  Bcast:172.16.27.255  Mask:255.255.255.0

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          Interrupt:18 Base address:0x1080 

/sbin/ifconfig eth0:1 down 

同樣CAP_NET_ADMIN可以讓普通用戶增加/刪除路由,如下:

/sbin/route add -host 192.168.27.139 gw 192.168.27.2

SIOCADDRT: Operation not permitted

授權NET_ADMIN,如下:

setcap cap_net_admin=eip /sbin/route

再次用普通用戶增加路由,如下:

/sbin/route add -host 192.168.27.139 gw 192.168.27.2

/sbin/route  -n

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

192.168.27.139  192.168.27.2    255.255.255.255 UGH   0      0        0 eth0

192.168.27.0    0.0.0.0         255.255.255.0   U     0      0        0 eth0

0.0.0.0         192.168.27.2    0.0.0.0         UG    0      0        0 eth0

/sbin/route del -host 192.168.27.139 gw 192.168.27.2   

我們看到我們除了可以增加路由之外,也可以刪除路由.

最后NET_ADMIN可以幫助我們讓普通用戶來管理防火牆.

普通用戶不能用iptables來管理防火牆,如下:

/sbin/iptables -L -n

iptables v1.4.2: can't initialize iptables table `filter': Permission denied (you must be root)

Perhaps iptables or your kernel needs to be upgraded.

我們將CAP_NET_ADMIN授權給iptables程序,注意我們也要將CAP_NET_RAW授權給iptables,CAP_NET_RAW我們后面再解釋,如下:

setcap cap_net_admin,cap_net_raw=eip /sbin/iptables-multi

此時就可以用普通用戶來管理防火牆了,如下:

/sbin/iptables -A INPUT -p tcp -j ACCEPT

/sbin/iptables -L -n

Chain INPUT (policy ACCEPT)

target     prot opt source               destination         

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           

Chain FORWARD (policy ACCEPT)

target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)

target     prot opt source               destination     

我們也可以刪除防火牆策略,並清空當前的數據流量,如下:

/sbin/iptables -F

/sbin/iptables -Z

/sbin/iptables -X

 

十八)CAP_NET_RAW 13 (允許使用原始(raw)套接字)

原始套接字編程可以接收到本機網卡上的數據幀或者數據包,對監控網絡流量和分析是很有作用的.

最常見的就是ping的實現,如下:

socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3

我們先把ping的setuid權限去掉

chmod u-s /bin/ping

ls -l /bin/ping

-rwxr-xr-x 1 root root 30788 2007-12-09 23:03 /bin/ping

用普通用戶使用ping

ping  192.168.27.2  

ping: icmp open socket: Operation not permitted

提示沒有權限,我們將ping授權CAP_NET_RAW能力,如下:

setcap cap_net_raw=eip /bin/ping

切換到普通用戶再次ping 192.168.27.2,發現可以ping通,如下:

ping  192.168.27.2

PING 192.168.27.2 (192.168.27.2) 56(84) bytes of data.

64 bytes from 192.168.27.2: icmp_seq=1 ttl=128 time=0.266 ms

64 bytes from 192.168.27.2: icmp_seq=2 ttl=128 time=0.280 ms

64 bytes from 192.168.27.2: icmp_seq=3 ttl=128 time=0.319 ms

NET_RAW也同樣用於tcpdump/iftop,一個普通用戶無法使用tcpdump/iftop,而CAP_NET_RAW可以解決這個問題,方式同ping一樣,所以我們不做演示.

十九)CAP_IPC_LOCK 14 (在允許鎖定內存片段)

root和普通用戶都可以用mlock來鎖定內存,區別是root不受ulimit下的鎖定內存大小限制,而普通用戶會受到影響.

測試程序如下:

復制代碼

 1 #include <stdio.h>
 2 #include <sys/mman.h>
 3 
 4 int main(int argc, char* argv[])
 5 {
 6         int array[2048];
 7 
 8         if (mlock((const void *)array, sizeof(array)) == -1) {
 9                 perror("mlock: ");
10                 return -1;
11         }
12 
13         printf("success to lock stack mem at: %p, len=%zd\n",
14                         array, sizeof(array));
15 
16 
17         if (munlock((const void *)array, sizeof(array)) == -1) {
18                 perror("munlock: ");
19                 return -1;
20         }
21 
22         printf("success to unlock stack mem at: %p, len=%zd\n",
23                         array, sizeof(array));
24 
25         return 0;
26 }

復制代碼

 

gcc mlock.c -o mlock

切換到普通用戶,我們看到mlock是不受限制

ulimit -a

max locked memory       (kbytes, -l) unlimited

我們運行程序

./mlock

success to lock stack mem at: 0xbfd94914, len=8192

success to unlock stack mem at: 0xbfd94914, len=8192

我們將限制改為1KB,再次運行程序,如下:

ulimit -l 1

./mlock

mlock: : Cannot allocate memory

切換到root用戶,將CAP_IPC_LOCK能力授權給mlock測試程序,如下:

setcap cap_ipc_lock=eip /tmp/mlock

用普通用戶再次運行程序,執行成功:

./mlock

success to lock stack mem at: 0xbfec1584, len=8192

success to unlock stack mem at: 0xbfec1584, len=8192

二十)CAP_IPC_OWNER 15 (忽略IPC所有權檢查)

這個能力對普通用戶有作用,如果用root用戶創建共享內存(shmget),權限為600,而普通用戶不能讀取該段共享內存.

通過CAP_IPC_OWNER可以讓普通用戶的程序可以讀取/更改共享內存.

我們用下面的程序創建共享內存段,並寫入0xdeadbeef到共享內存段中.

復制代碼

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <sys/ipc.h>
 6 #include <sys/shm.h>
 7 #include <sys/wait.h>
 8 
 9 void error_out(const char *msg)
10 {
11         perror(msg);
12         exit(EXIT_FAILURE);
13 }
14 
15 
16 int main (int argc, char *argv[])
17 {
18         key_t mykey = 12345678;
19 
20         const size_t region_size = sysconf(_SC_PAGE_SIZE);
21         int smid = shmget(mykey, region_size, IPC_CREAT|0600);
22         if(smid == -1)
23                 error_out("shmget");
24 
25         void *ptr;
26         ptr = shmat(smid, NULL, 0);
27         if (ptr == (void *) -1)
28                 error_out("shmat");
29         u_long *d = (u_long *)ptr;
30         *d = 0xdeadbeef;
31         printf("ipc mem %#lx\n", *(u_long *)ptr);
32 
33         return 0;
34 }

復制代碼

gcc test.c -o test -lrt

我們用root用戶來執行本程序,創建共享內存,如下:

/tmp/test

ipc mem 0xdeadbeef

查看當前的共享內存

ipcs -m

------ Shared Memory Segments --------

key        shmid      owner      perms      bytes      nattch     status      

0x00bc614e 458752     root      600        4096       0              

修改程序,將*d = 0xdeadbeef;改為*d = 0xffffffff;

再編譯,用普通用戶運行程序,如下:

gcc test.c -o test -lrt

su - test

/tmp/test

shmget: Permission denied

我們看到沒有權限更改共享內存.

用root用戶把CAP_IPC_OWNER能力授權給test程序,如下:

setcap cap_ipc_owner=eip /tmp/test

再次用普通用戶運行程序test,更改共享內存,如下:

/tmp/test

ipc mem 0xffffffff

我們看到修改成功,但要說明CAP_IPC_OWNER不能讓進程/程序刪除/脫離共享內存

二十一)CAP_SYS_MODULE 16 (允許普通用戶插入和刪除內核模塊)

由於普通用戶不能插入/刪除內核模塊,而CAP_SYS_MODULE可以幫助普通用戶做到這點

例如,用戶test插入內核模塊nvram

/sbin/modprobe nvram      

FATAL: Error inserting nvram (/lib/modules/2.6.38.4/kernel/drivers/char/nvram.ko): Operation not permitted

系統提示權限不足.

我們把CAP_SYS_MODULE能力授權給modprobe程序,如下:

setcap cap_sys_module=eip /sbin/modprobe

再用普通用戶加載內核模塊,如下:

/sbin/modprobe nvram

lsmod |grep nvram

nvram                   3861  0 

我們看到可以加載.

同樣我們可以將CAP_SYS_MODULE授權給rmmod程序,讓其可以刪除模塊,如下:

setcap cap_sys_module=eip /sbin/rmmod

su - test

/sbin/rmmod nvram

lsmod |grep nvram

二十二)CAP_SYS_RAWIO 17 (允許用戶打開端口,並讀取修改端口數據,一般用ioperm/iopl函數)

ioperm只有低端的[0-0x3ff] I/O端口可被設置,且普通用戶不能使用.

iopl可以用於所有的65536個端口,因此ioperm相當於iopl調用的一個子集.

下面的程序首先設置0x3FF端口的讀寫權限,然后讀出原先的值,然后將原值的LSB翻轉並寫回端口,並在此讀取端口值.

復制代碼

 1 #include <unistd.h>
 2 #include <sys/io.h>
 3 
 4 #define PORT_ADDR 0x3FF
 5 
 6 int main(void)
 7 {
 8       int ret;
 9       char port_val;
10       
11       /*set r/w permission of all 65536 ports*/
12       ret = iopl(3);
13       if(ret < 0){
14            perror("iopl set error");
15            return 0;
16       }
17 
18       port_val = inb(PORT_ADDR);
19       printf("Original value of port 0x%x is : %.2x\n", PORT_ADDR, port_val);
20       
21       /*reverse the least significant bit */
22 
23       outb(port_val^0x01, PORT_ADDR);
24       port_val = inb(PORT_ADDR);
25       printf("Current value of port 0x%x is : %.2x\n", PORT_ADDR, port_val);
26       
27       /*set r/w permission of  all 65536 ports*/
28       
29       ret = iopl(0);
30       if(ret < 0){
31            perror("iopl set error");
32            return 0;
33       }
34       return 0;
35 }

復制代碼

編譯:

gcc iopl.c -o iopl

普通用戶運行iopl程序,提示沒有權限.

/tmp/iopl 

iopl set error: Operation not permitted

給程序iopl授權CAP_SYS_RAWIO能力,此時普通用戶可以執行iopl程序,如下:

setcap cap_sys_rawio=eip /tmp/iopl

su - test

/tmp/iopl 

Original value of port 0x3ff is : 01

Current value of port 0x3ff is : 00

/tmp/iopl 

Original value of port 0x3ff is : 00

Current value of port 0x3ff is : 01

二十三)CAP_SYS_CHROOT 18 (允許使用chroot()系統調用)

普通用戶不能通過chroot系統調用更改程式執行時所參考的根目錄位置,而CAP_SYS_CHROOT可以幫助普通用戶做到這一點.

普通用戶使用chroot,如下:

/usr/sbin/chroot / /bin/bash

/usr/sbin/chroot: cannot change root directory to /: Operation not permitted

通過root授權CAP_SYS_CHROOT能力給chroot程序,如下:

capset cap_sys_chroot=eip /usr/sbin/chroot

普通用戶再次用chroot切換根目錄,如下:

/usr/sbin/chroot / /bin/sh

sh-3.2$

二十四)CAP_SYS_PTRACE 19 (允許跟蹤任何進程)

普通用戶不能跟蹤任何進程,不包括它自己的進程,而CAP_SYS_PTRACE可以幫助普通用戶跟蹤任何進程.

切換到普通用戶,跟蹤PID1的進程.

strace -p 1

attach: ptrace(PTRACE_ATTACH, ...): Operation not permitted

切換到root用戶,將CAP_SYS_PTRACE能力授權給strace程序,用於跟蹤進程,如下:

setcap cap_sys_ptrace=eip /usr/bin/strace

切換到普通用戶,跟蹤PID1的進程,如下:

strace -p 1

Process 1 attached - interrupt to quit

select(11, [10], NULL, NULL, {3, 771381}) = 0 (Timeout)

time(NULL)                              = 1304348451

stat64("/dev/initctl", {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0

fstat64(10, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0

stat64("/dev/initctl", {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0

select(11, [10], NULL, NULL, {5, 0})    = 0 (Timeout)

time(NULL)                              = 1304348456

二十五)CAP_SYS_PACCT 20 (允許配置進程記帳process accounting) 

要完成進程記帳,要保證有寫入文件的權限,這里我們將進程記錄寫入到/home/test/log/psacct.

mkdir /home/test/log/

touch /home/test/log/psacct

程序通過acct函數,將進程的記帳寫入到指定文件中,如果acct函數的參數為NULL,則關閉進程記帳.

復制代碼

 1 #include <stdlib.h>
 2 #include <sys/acct.h>
 3 
 4 int
 5 main()
 6 {
 7         int ret;
 8         ret = acct("/home/test/log/pacct");
 9         if(ret < 0){
10            perror("acct on error");
11            return 0;
12         }
13         system("/bin/ls -l");
14         acct(NULL);
15         if(ret < 0){
16            perror("acct off error");
17            return 0;
18         }
19         return 0;
20 }

復制代碼

 

gcc psacct.c -o psacct

./psacct 

acct on error: Operation not permitted

給psacct程序授權CAP_SYS_PACCT能力,如下:

setcap cap_sys_psacct /home/test/psacct

注意這里的能力是sys_psacct,而不是sys_pacct

回到普通用戶,執行psacct

./psacct 

total 24

drwxr-xr-x 2 test test 4096 2011-05-02 13:28 log

-rw-r--r-- 1 test test   64 2011-05-02 13:25 pacct

-rwxr-xr-x 1 test test 6590 2011-05-02 13:31 psacct

-rw-r--r-- 1 test test  314 2011-05-02 13:31 psacct.c

我們看到已經執行成功,下面我們再看下進程記錄,如下:

lastcomm -f /home/test/log/pacct 

ls                     test     pts/0      0.03 secs Mon May  2 13:33

二十六)CAP_SYS_ADMIN 21 (允許執行系統管理任務,如掛載/卸載文件系統,設置磁盤配額,開/關交換設備和文件等)

下面是普通用戶擁有相關管理權限的測試

1)更改主機名

setcap cap_sys_admin=eip /bin/hostname

su - test

hostname test2

hostname

test2

2)掛載/卸載文件系統

這里我們注意,系統的mount命令不能做這個試驗,因為程序中做了判斷,如果不是root用戶請退出.所以我們用mount()函數來完成這個試驗,程序如下:

復制代碼

 1 #include <stdlib.h>
 2 #include <sys/mount.h>
 3 int
 4 main ()
 5 {
 6         int ret;
 7         ret = mount("/dev/sda1", "/mnt/", "ext3", MS_MGC_VAL, NULL);
 8         if(ret < 0){
 9            perror("mount error");
10            return 0;
11         }
12         return 0;
13 }

復制代碼

 

用普通用戶編譯運行:

gcc mounttest.c -o mounttest

./mounttest 

mount error: Operation not permitted

我們看到普通用戶不能完成mount操作,而CAP_SYS_ADMIN可以幫助普通用戶完成該操作,如下:

setcap cap_sys_admin=eip /home/test/mounttest 

切換到普通用戶,執行mounttest程序,如下:

./mounttest 

cat /proc/mounts 

/dev/sda1 /mnt ext3 rw,relatime,errors=remount-ro,barrier=0,data=writeback 0 0

umount和mount一樣,我們在這里不做演示.

3)swapon/swapoff

普通用戶不能進行swapon/swapoff操作,而CAP_SYS_ADMIN可以幫助普通用戶完成swapon/swapoff操作,如下:

dd if=/dev/zero of=/tmp/testdb bs=10M count=1

1+0 records in

1+0 records out

10485760 bytes (10 MB) copied, 0.164669 s, 63.7 MB/s

/sbin/mkswap /tmp/testdb

Setting up swapspace version 1, size = 10481 kB

no label, UUID=0ff46dc8-781c-4c3f-81b3-fe860f74793e

/sbin/swapon /tmp/testdb

swapon: /tmp/testdb: Operation not permitted

我們看到swapon操作被拒絕,這里我們對swapon進行授權,如下:

setcap cap_sys_admin /sbin/swapon

普通用戶再次swapon,如下:

/sbin/swapon /tmp/testdb

/sbin/swapon 

/sbin/swapon -s

Filename                                Type            Size    Used    Priority

/dev/sda6                               partition       7815584 0       -1

/tmp/testdb                             file            10236   0       -2

我們看到swapon操作成功.因為swapoff是swapon的軟鏈接,所以可以直接swapoff掉交換分區,如下:

/sbin/swapoff /tmp/testdb

/sbin/swapon -s

Filename                                Type            Size    Used    Priority

/dev/sda6                               partition       7815584 0       -1

ls -l /sbin/swapoff    

lrwxrwxrwx 1 root root 6 2009-08-23 07:49 /sbin/swapoff -> swapon

二十七) CAP_SYS_BOOT 22 (允許普通用使用reboot()函數)

這里我們無法用reboot命令來做測試,因為reboot命令做了判斷,只允許root(UID=0)的用戶可以使用.

我們用下面的程序進行測試.

復制代碼

1 #include <unistd.h>
2 #include <sys/reboot.h>
3 int main()
4 {
5     sync(); 
6     return reboot(RB_AUTOBOOT);
7 }

復制代碼

編譯:

gcc reboot1.c -o reboot1

./reboot1

這時系統沒有重啟.

我們查看程序的返回碼,這里是255,說明程序沒有運行成功.

echo $?

255

我們用CAP_SYS_BOOT能力使reboot1程序可以被普通用戶重啟,如下:

setcap cap_sys_boot=eip /home/test/reboot1

用普通用戶再試運行reboot1,如下:

reboot1

此時系統被重啟了.

二十八)CAP_SYS_NICE 23(允許提升優先級,設置其它進程的優先級)

對於普通用戶程序的NICE優先級,不能超過ulimit對它的限制,如下:

nice -n -5 ls 

nice: cannot set niceness: Permission denied

而CAP_SYS_NICE可以幫助普通用戶設置一個想要的一個任意優先級.

setcap cap_sys_nice=eip /usr/bin/nice

切換到普通用戶,指定優先級,如下:

nice -n -5 ls 

log  mnt  mount.c  mounttest  pacct  psacct  psacct.c  reboot1  reboot1.c  test

普通用戶也不能給指定進程指定NICE優先級,而CAP_SYS_NICE也可以做的,如下:

renice -5 2255

renice: 2255: setpriority: Operation not permitted

setcap cap_sys_nice=eip /usr/bin/renice

renice -5 2255

2255: old priority 0, new priority -5

我們甚至可以用CAP_SYS_NICE來指定實時優先級

chrt -f 50  ls

chrt: failed to set pid 0's policy: Operation not permitted

給/usr/bin/chrt命令授權CAP_SYS_NICE能力,如下:

setcap  cap_sys_nice=eip  /usr/bin/chrt

切換到普通用戶,如下:

chrt -f 50  ls

log  mnt  setulimit  setulimit.c

我們也可以指定它的CPU親和性,如下:

taskset -p 1 2255

pid 2255's current affinity mask: 1

sched_setaffinity: Operation not permitted

failed to set pid 2255's affinity.

給/usr/bin/taskset命令授權CAP_SYS_NICE能力,如下:

setcap  cap_sys_nice=eip  /usr/bin/taskset

切換到普通用戶,再次運行taskset,可以成功的設置CPU親和性

taskset -p 1 2255

pid 2255's current affinity mask: 1

pid 2255's new affinity mask: 1

二十九) CAP_SYS_RESOURCE 24 忽略資源限制

普通用戶不能用setrlimit()來突破ulimit的限制

我們用下面的程序進行測試,普通用戶是無法修改RLIMIT_STACK(堆棧大小)的.如下:

復制代碼

 1 #include <signal.h>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <stdlib.h>
 5 #include <limits.h>
 6 #include <unistd.h>
 7 #include <sys/types.h>
 8 #include <sys/stat.h>
 9 #include <sys/resource.h>
10 
11 int
12 main (int argc, char *argv[])
13 {
14 int r = 0;
15 struct rlimit rl;
16 
17 getrlimit (RLIMIT_STACK,&rl);
18 
19 printf("crrent hard limit is %ld\n",
20 (u_long) rl.rlim_max);
21 
22 rl.rlim_max = rl.rlim_max+1;
23 r = setrlimit (RLIMIT_STACK, &rl);
24 if (r){
25 perror("setrlimit");
26 return -1;
27 }
28 
29 printf("limit set to %ld \n", (u_long) rl.rlim_max+1);
30 return 0;
31 }

復制代碼

 

gcc setulimit.c -o setulimit

我們先來查看當前的限制,這里是10MB,如下:

ulimit -H -s 

10240

./setulimit    

crrent hard limit is 10485760

setrlimit: Operation not permitted

我們給setulimit程序以CAP_SYS_RESOURCE的能力,如下:

setcap cap_sys_resource=eip /home/test/setulimit 

用普通用戶再次運行程序,已經可以通過setrlimit()設定超過限額了,如下:

./setulimit 

crrent hard limit is 10485760

limit set to 10485762 

同樣我們也可以用CAP_SYS_RESOURCE能力使程序超出磁盤限額,如下:

quotacheck -avug

quotaon -avug

edquota -u test

Filesystem                   blocks       soft       hard     inodes     soft     hard

  /dev/sda7                         0          3          5          0        0        0

mkdir /export/test

chown -R test.test /export/test/

切換到普通用戶,用dd命令產生3MB的文件,這明顯超過了5kb的限制,如下:

su - test

dd if=/dev/zero of=/export/test/test bs=1M count=3

sda7: warning, user block quota exceeded.

sda7: write failed, user block limit reached.

dd: writing `test': Disk quota exceeded

1+0 records in

0+0 records out

4096 bytes (4.1 kB) copied, 0.0117371 s, 349 kB/s

授權CAP_SYS_RESOURCE能力給/bin/dd命令,如下:

setcap cap_sys_resource=eip /bin/dd

再次用普通用戶運行dd命令,即可以超過5kb的限額了.

su - test

dd if=/dev/zero of=test bs=1M count=3

sda7: warning, user block quota exceeded.

3+0 records in

3+0 records out

3145728 bytes (3.1 MB) copied, 0.423662 s, 7.4 MB/s

三十)CAP_SYS_TIME 25(允許改變系統時鍾)

普通用戶不能改變系統時鍾,如下:

date -s 2012-01-01

date: cannot set date: Operation not permitted

Sun Jan  1 00:00:00 EST 2012

CAP_SYS_TIME可以幫助普通用戶改變系統時鍾,如下:

setcap cap_sys_time=eip /bin/date

切換到普通用戶再次改變時間,發現已經可以改變了

su - test

date -s 2012-01-01

Sun Jan  1 00:00:00 EST 2012

date

Sun Jan  1 00:00:02 EST 2012

三十一)CAP_SYS_TTY_CONFIG 26(允許配置TTY設備)

我們下面用vhangup()函數來掛起當前的tty

程序如下:

復制代碼

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 
 4 int main ()
 5 {
 6   int r;
 7   r=vhangup();
 8   if (r){ 
 9   perror ("vhanguo");
10   }
11   return 0;
12 }

復制代碼

gcc vhup.c -o vhup

./vhup 

vhanguo: Operation not permitted

我們給vhup程序設定CAP_SYS_TTY_CONFIG能力,如下:

setcap cap_sys_tty_config=eip /home/test/vhup

再次用普通用戶執行程序vhup,tty被掛起,如下:

./vhup 

此時當前tty被掛起.

三十二) CAP_MKNOD 27 (允許使用mknod系統調用)

普通用戶不能用mknod()來創建設備文件,而CAP_MKNOD可以幫助普通用戶做到這一點,如下:

mknod /tmp/tnod1 c 1 5

mknod: `/tmp/tnod1': Operation not permitted

setcap cap_mknod=eip /bin/mknod

切換到普通用戶,再次用mknod命令創建設備文件,如下:

mknod /tmp/tnod1 c 1 5

ls -l /tmp/tnod1 

crw-r--r-- 1 test test 1, 5 2012-01-01 00:31 /tmp/tnod1

三十三) CAP_LEASE 28(允許在文件上建立租借鎖)

系統調用fcntl()可以用於租借鎖,此時采用的函數原型如下:

       int fcntl(int fd, int cmd, long arg);

與租借鎖相關的 cmd 參數的取值有兩種:F_SETLEASE 和 F_GETLEASE。其含義如下所示:

F_SETLEASE:根據下面所描述的 arg 參數指定的值來建立或者刪除租約:

F_RDLCK:設置讀租約。當文件被另一個進程以寫的方式打開時,擁有該租約的當前進程會收到通知

F_WRLCK:設置寫租約。當文件被另一個進程以讀或者寫的方式打開時,擁有該租約的當前進程會收到通知

F_UNLCK:刪除以前建立的租約

F_GETLEASE:表明調用進程擁有文件上哪種類型的鎖,這需要通過返回值來確定,返回值有三種:F_RDLCK、F_WRLCK和F_UNLCK,分別表明調用進程對文件擁有讀租借、寫租借或者根本沒有租借

某個進程可能會對文件執行其他一些系統調用(比如 OPEN() 或者 TRUNCATE()),如果這些系統調用與該文件上由 F_SETLEASE 所設置的租借鎖相沖突,內核就會阻塞這個系統調用;

同時,內核會給擁有這個租借鎖的進程發信號,告知此事。擁有此租借鎖的進程會對該信號進行反饋,它可能會刪除這個租借鎖,也可能會減短這個租借鎖的租約,從而可以使得該文件可以被其他進程所訪問。

如果擁有租借鎖的進程不能在給定時間內完成上述操作,那么系統會強制幫它完成。通過 F_SETLEASE 命令將 arg 參數指定為 F_UNLCK 就可以刪除這個租借鎖。

不管對該租借鎖減短租約或者干脆刪除的操作是進程自願的還是內核強迫的,只要被阻塞的系統調用還沒有被發出該調用的進程解除阻塞,那么系統就會允許這個系統調用執行。

即使被阻塞的系統調用因為某些原因被解除阻塞,但是上面對租借鎖減短租約或者刪除這個過程還是會執行的。

源程序如下:

復制代碼

 1 #define _GNU_SOURCE 
 2 
 3 #include <unistd.h>
 4 #include <fcntl.h>
 5 #include <stdio.h>
 6 #include <sys/file.h>
 7 static void show_lease(int fd)
 8 {
 9         int res;
10 
11         res = fcntl(fd, F_GETLEASE);
12         switch (res) {
13                 case F_RDLCK:
14                         printf("Read lease\n");
15                         break;
16                 case F_WRLCK:
17                         printf("Write lease\n");
18                         break;
19                 case F_UNLCK:
20                         printf("No leases\n");
21                         break;
22                 default:
23                         printf("Some shit\n");
24                         break;
25         }
26 }
27 
28 int main(int argc, char **argv)
29 {
30         int fd, res;
31 
32         fd = open(argv[1], O_RDONLY);
33         if (fd == -1) {
34                 perror("Can't open file");
35                 return 1;
36         }
37 
38         res = fcntl(fd, F_SETLEASE, F_WRLCK);
39         if (res == -1) {
40                 perror("Can't set lease");
41                 return 1;
42         }
43 
44         show_lease(fd);
45 
46         if (flock(fd, LOCK_SH) == -1) {
47                 perror("Can't flock shared");
48                 return 1;
49         }
50 
51         show_lease(fd);
52 
53         return 0;
54 }

復制代碼

編譯:

gcc fcntl.c -o fcntl

我們使用普通用戶在/etc/passwd文件上建立租借鎖,由於普通用戶沒有/etc/passwd上建租借鎖的權限,故而報錯,如下:

su - test

/tmp/fcntl /etc/passwd

Can't set lease: Permission denied

注:

普通用戶可以在自己的文件上(owner)建立租借鎖.

授權lease給/tmp/fcntl文件,如下:

setcap cap_lease=eip /tmp/fcntl

再次運行/tmp/fcntl,可以建立租借鎖了,如下:

/tmp/fcntl /etc/passwd

Write lease

Write lease

三十四)CAP_SETFCAP 31 (允許在指定的程序上授權能力給其它程序)

例如我們讓普通用戶也能用setcap給其它程序授權,如下:

setcap CAP_SETFCAP=eip /usr/sbin/setcap 

su - test

setcap CAP_SETPCAP=eip /bin/ls

getcap /bin/ls                

/bin/ls = cap_setpcap+eip

三十六)其它的能力

CAP_AUDIT_WRITE

CAP_AUDIT_CONTROL

CAP_MAC_OVERRIDE

CAP_MAC_ADMIN

CAP_SYSLOG

這五組只能涉及到syslog,audit,mac等安全模塊,以后專門對其進行分析.

三十七)總結:

CAP_CHOWN 0 允許改變文件的所有權 

CAP_DAC_OVERRIDE 1 忽略對文件的所有DAC訪問限制 

CAP_DAC_READ_SEARCH 2 忽略所有對讀、搜索操作的限制 

CAP_FOWNER 3 以最后操作的UID,覆蓋文件的先前的UID

CAP_FSETID 4 確保在文件被修改后不修改setuid/setgid位

CAP_KILL 5 允許對不屬於自己的進程發送信號 

CAP_SETGID 6 允許改變組ID 

CAP_SETUID 7 允許改變用戶ID 

CAP_SETPCAP 8 允許向其它進程轉移能力以及刪除其它進程的任意能力(只限init進程)

CAP_LINUX_IMMUTABLE 9 允許修改文件的不可修改(IMMUTABLE)和只添加(APPEND-ONLY)屬性 

CAP_NET_BIND_SERVICE 10 允許綁定到小於1024的端口 

CAP_NET_BROADCAST 11 允許網絡廣播和多播訪問(未使用) 

CAP_NET_ADMIN 12 允許執行網絡管理任務:接口、防火牆和路由等.

CAP_NET_RAW 13 允許使用原始(raw)套接字 

CAP_IPC_LOCK 14 允許鎖定共享內存片段 

CAP_IPC_OWNER 15 忽略IPC所有權檢查 

CAP_SYS_MODULE 16 插入和刪除內核模塊 

CAP_SYS_RAWIO 17 允許對ioperm/iopl的訪問 

CAP_SYS_CHROOT 18 允許使用chroot()系統調用 

CAP_SYS_PTRACE 19 允許跟蹤任何進程 

CAP_SYS_PACCT 20 允許配置進程記帳(process accounting) 

CAP_SYS_ADMIN 21 允許執行系統管理任務:加載/卸載文件系統、設置磁盤配額、開/關交換設備和文件等.

CAP_SYS_BOOT 22 允許重新啟動系統 

CAP_SYS_NICE 23 允許提升優先級,設置其它進程的優先級 

CAP_SYS_RESOURCE 24 忽略資源限制 

CAP_SYS_TIME 25 允許改變系統時鍾 

CAP_SYS_TTY_CONFIG 26 允許配置TTY設備 

CAP_MKNOD 27 允許使用mknod()系統調用 

CAP_LEASE 28 允許在文件上建立租借鎖

CAP_SETFCAP 31 允許在指定的程序上授權能力給其它程序

參考:

man capabilities

linux-2.6.38.5/include/linux/capability.h 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM