zoukankan      html  css  js  c++  java
  • Linux的capability深入分析(1)

    一)概述:

     
     
    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的设定与清除
     
    我们在下面的程序中给当前的进程设定能力,最后我们清除掉所设定的能力,源程序如下:
    [cpp] view plaincopy
     
    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 ", 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 ",(int) getpid(), cap_to_text(caps, &y));  
    22.     fflush(0);  
    23.     cap_free(caps);  
    24. }  
    25.     
    26. int main(int argc, char **argv)  
    27. {  
    28.     int stat;  
    29.     whoami();  
    30.     stat = setuid(geteuid());  
    31.     pid_t parentPid = getpid();  
    32.   
    33.     if(!parentPid)  
    34.     return 1;  
    35.     cap_t caps = cap_init();  
    36.   
    37.     cap_value_t capList[5] ={ CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP } ;  
    38.     unsigned num_caps = 5;  
    39.     cap_set_flag(caps, CAP_EFFECTIVE, num_caps, capList, CAP_SET);  
    40.     cap_set_flag(caps, CAP_INHERITABLE, num_caps, capList, CAP_SET);  
    41.     cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);  
    42.    
    43.     if (cap_set_proc(caps)) {  
    44.         perror("capset()");  
    45.    
    46.         return EXIT_FAILURE;  
    47.     }  
    48.     listCaps();  
    49.   
    50.     printf("dropping caps ");  
    51.     cap_clear(caps);  // resetting caps storage  
    52.   
    53.     if (cap_set_proc(caps)) {  
    54.         perror("capset()");  
    55.         return EXIT_FAILURE;  
    56.     }  
    57.     listCaps();  
    58.   
    59.     cap_free(caps);  
    60.     return 0;  
    61. }  
    编译:
    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来设定掩码,下面获取掩码的体现:
    [cpp] view plaincopy
     
    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 ", cap_data->effective,cap_data->permitted, cap_data->inheritable);  
    25. }  
    gcc capget0.c -o capget0 -lcap
     
    普通用户:
    ./capget0 
    Cap data 0x0, 0x0, 0x0
     
    超级用户:
    /home/test/capget0 
    Cap data 0xffffffff, 0xffffffff, 0x0
     
    这也说明了默认情况下,root运行的进程是什么权限都有,而普通用户则什么权限都没有.
     
     
    我们可以将本程序与上面的程序进行整合,如下:
    [cpp] view plaincopy
     
    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 ", 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 ",(int) getpid(), cap_to_text(caps, &y));  
    22.     fflush(0);  
    23.     cap_free(caps);  
    24. }  
    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.     cap_value_t capList[5] ={ CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP } ;  
    39.     unsigned num_caps = 5;  
    40.     cap_set_flag(caps, CAP_EFFECTIVE, num_caps, capList, CAP_SET);  
    41.     cap_set_flag(caps, CAP_INHERITABLE, num_caps, capList, CAP_SET);  
    42.     cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);  
    43.   
    44.     if (cap_set_proc(caps)) {  
    45.         perror("capset()");  
    46.         return EXIT_FAILURE;  
    47.     }  
    48.     listCaps();  
    49.   
    50.     cap_free(caps);  
    51.   
    52.     struct __user_cap_header_struct cap_header_data;  
    53.     cap_user_header_t cap_header = &cap_header_data;  
    54.   
    55.     struct __user_cap_data_struct cap_data_data;  
    56.     cap_user_data_t cap_data = &cap_data_data;  
    57.   
    58.     cap_header->pid = getpid();  
    59.     cap_header->version = _LINUX_CAPABILITY_VERSION_1;  
    60.   
    61.     if (capget(cap_header, cap_data) < 0) {  
    62.         perror("Failed capget");  
    63.         exit(1);  
    64.     }  
    65.     printf("Cap data 0x%x, 0x%x, 0x%x ", cap_data->effective,cap_data->permitted, cap_data->inheritable);  
    66.     sleep(60);  
    67.     return 0;  
    68. }  
    编译并执行:
    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是系统的边界能力,我们无法改变它.
  • 相关阅读:
    深入浅出 Java 8 Lambda 表达式
    OneAPM x 腾讯 | OneAPM 技术公开课·深圳 报名:前端性能大作战!
    第30节:Java基础-内部类
    第二十九节:Java基础知识-类,多态,Object,数组和字符串
    第二十九节:Java基础知识-类,多态,Object,数组和字符串
    第二十九节:Java基础知识-类,多态,Object,数组和字符串
    第二十八节:Java基础-进阶继承,抽象类,接口
    第二十八节:Java基础-进阶继承,抽象类,接口
    第二十八节:Java基础-进阶继承,抽象类,接口
    ES6教程-字符串,函数的参数,了解函数的arguments对象,js面向对象,设计模式-单例模式,解构赋值
  • 原文地址:https://www.cnblogs.com/fengwei/p/4520876.html
Copyright © 2011-2022 走看看