一)概述:linux
1)從2.1版開始,Linux內核有了能力(capability)的概念,即它打破了UNIX/LINUX操做系統中超級用戶/普通用戶的概念,由普通用戶也能夠作只有超級用戶能夠完成的工做.
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的設定與清除
咱們在下面的程序中給當前的進程設定能力,最後咱們清除掉所設定的能力,源程序以下:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <unistd.h>
-
- #undef _POSIX_SOURCE
- #include <sys/capability.h>
-
- extern int errno;
-
- void whoami(void)
- {
- printf("uid=%i euid=%i gid=%i\n", getuid(), geteuid(), getgid());
- }
-
- void listCaps()
- {
- cap_t caps = cap_get_proc();
- ssize_t y = 0;
- printf("The process %d was give capabilities %s\n",(int) getpid(), cap_to_text(caps, &y));
- fflush(0);
- cap_free(caps);
- }
-
- int main(int argc, char **argv)
- {
- int stat;
- whoami();
- stat = setuid(geteuid());
- pid_t parentPid = getpid();
-
- if(!parentPid)
- return 1;
- cap_t caps = cap_init();
-
- cap_value_t capList[5] ={ CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP } ;
- unsigned num_caps = 5;
- 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("capset()");
-
- return EXIT_FAILURE;
- }
- listCaps();
-
- printf("dropping caps\n");
- cap_clear(caps);
-
- if (cap_set_proc(caps)) {
- perror("capset()");
- return EXIT_FAILURE;
- }
- listCaps();
-
- cap_free(caps);
- return 0;
- }
編譯:
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來設定掩碼,下面獲取掩碼的體現:
- #undef _POSIX_SOURCE
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <linux/capability.h>
- #include <errno.h>
-
- int main()
- {
- 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 0x%x, 0x%x, 0x%x\n", cap_data->effective,cap_data->permitted, cap_data->inheritable);
- }
gcc capget0.c -o capget0 -lcap
普通用戶:
./capget0
Cap data 0x0, 0x0, 0x0
超級用戶:
/home/test/capget0
Cap data 0xffffffff, 0xffffffff, 0x0
這也說明了默認狀況下,root運行的進程是什麼權限都有,而普通用戶則什麼權限都沒有.
咱們能夠將本程序與上面的程序進行整合,以下:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <unistd.h>
-
- #undef _POSIX_SOURCE
- #include <sys/capability.h>
-
- extern int errno;
-
- void whoami(void)
- {
- printf("uid=%i euid=%i gid=%i\n", getuid(), geteuid(), getgid());
- }
-
- void listCaps()
- {
- cap_t caps = cap_get_proc();
- ssize_t y = 0;
- printf("The process %d was give capabilities %s\n",(int) getpid(), cap_to_text(caps, &y));
- fflush(0);
- cap_free(caps);
- }
-
-
- int main(int argc, char **argv)
- {
- int stat;
- whoami();
- stat = setuid(geteuid());
- pid_t parentPid = getpid();
-
- if(!parentPid)
- return 1;
- cap_t caps = cap_init();
-
- cap_value_t capList[5] ={ CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP } ;
- unsigned num_caps = 5;
- 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("capset()");
- return EXIT_FAILURE;
- }
- listCaps();
-
- cap_free(caps);
-
- 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 0x%x, 0x%x, 0x%x\n", cap_data->effective,cap_data->permitted, cap_data->inheritable);
- sleep(60);
- return 0;
- }
編譯並執行:
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是系統的邊界能力,咱們沒法改變它.