一)概述:

 
 
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的设定与清除
 
我们在下面的程序中给当前的进程设定能力,最后我们清除掉所设定的能力,源程序如下:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <sys/types.h>
  5. #include <unistd.h>
  6. #undef _POSIX_SOURCE
  7. #include <sys/capability.h>
  8. extern int errno;
  9. void whoami(void)
  10. {
  11. printf("uid=%i  euid=%i  gid=%i\n", getuid(), geteuid(), getgid());
  12. }
  13. void listCaps()
  14. {
  15. cap_t caps = cap_get_proc();
  16. ssize_t y = 0;
  17. printf("The process %d was give capabilities %s\n",(int) getpid(), cap_to_text(caps, &y));
  18. fflush(0);
  19. cap_free(caps);
  20. }
  21. int main(int argc, char **argv)
  22. {
  23. int stat;
  24. whoami();
  25. stat = setuid(geteuid());
  26. pid_t parentPid = getpid();
  27. if(!parentPid)
  28. return 1;
  29. cap_t caps = cap_init();
  30. cap_value_t capList[5] ={ CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP } ;
  31. unsigned num_caps = 5;
  32. cap_set_flag(caps, CAP_EFFECTIVE, num_caps, capList, CAP_SET);
  33. cap_set_flag(caps, CAP_INHERITABLE, num_caps, capList, CAP_SET);
  34. cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);
  35. if (cap_set_proc(caps)) {
  36. perror("capset()");
  37. return EXIT_FAILURE;
  38. }
  39. listCaps();
  40. printf("dropping caps\n");
  41. cap_clear(caps);  // resetting caps storage
  42. if (cap_set_proc(caps)) {
  43. perror("capset()");
  44. return EXIT_FAILURE;
  45. }
  46. listCaps();
  47. cap_free(caps);
  48. return 0;
  49. }
编译:
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. int main()
  9. {
  10. struct __user_cap_header_struct cap_header_data;
  11. cap_user_header_t cap_header = &cap_header_data;
  12. struct __user_cap_data_struct cap_data_data;
  13. cap_user_data_t cap_data = &cap_data_data;
  14. cap_header->pid = getpid();
  15. cap_header->version = _LINUX_CAPABILITY_VERSION_1;
  16. if (capget(cap_header, cap_data) < 0) {
  17. perror("Failed capget");
  18. exit(1);
  19. }
  20. printf("Cap data 0x%x, 0x%x, 0x%x\n", cap_data->effective,cap_data->permitted, cap_data->inheritable);
  21. }
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. #undef _POSIX_SOURCE
  7. #include <sys/capability.h>
  8. extern int errno;
  9. void whoami(void)
  10. {
  11. printf("uid=%i  euid=%i  gid=%i\n", getuid(), geteuid(), getgid());
  12. }
  13. void listCaps()
  14. {
  15. cap_t caps = cap_get_proc();
  16. ssize_t y = 0;
  17. printf("The process %d was give capabilities %s\n",(int) getpid(), cap_to_text(caps, &y));
  18. fflush(0);
  19. cap_free(caps);
  20. }
  21. int main(int argc, char **argv)
  22. {
  23. int stat;
  24. whoami();
  25. stat = setuid(geteuid());
  26. pid_t parentPid = getpid();
  27. if(!parentPid)
  28. return 1;
  29. cap_t caps = cap_init();
  30. cap_value_t capList[5] ={ CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP } ;
  31. unsigned num_caps = 5;
  32. cap_set_flag(caps, CAP_EFFECTIVE, num_caps, capList, CAP_SET);
  33. cap_set_flag(caps, CAP_INHERITABLE, num_caps, capList, CAP_SET);
  34. cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);
  35. if (cap_set_proc(caps)) {
  36. perror("capset()");
  37. return EXIT_FAILURE;
  38. }
  39. listCaps();
  40. cap_free(caps);
  41. struct __user_cap_header_struct cap_header_data;
  42. cap_user_header_t cap_header = &cap_header_data;
  43. struct __user_cap_data_struct cap_data_data;
  44. cap_user_data_t cap_data = &cap_data_data;
  45. cap_header->pid = getpid();
  46. cap_header->version = _LINUX_CAPABILITY_VERSION_1;
  47. if (capget(cap_header, cap_data) < 0) {
  48. perror("Failed capget");
  49. exit(1);
  50. }
  51. printf("Cap data 0x%x, 0x%x, 0x%x\n", cap_data->effective,cap_data->permitted, cap_data->inheritable);
  52. sleep(60);
  53. return 0;
  54. }
编译并执行:
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是系统的边界能力,我们无法改变它.

Linux的capability深入分析(1)的更多相关文章

  1. Linux的capability深入分析

    Linux的capability深入分析详见:http://blog.csdn.net/u014338577/article/details/48791953 lxd中对容器能力的限制: 普通用户不能 ...

  2. Linux的capability深入分析(2)【转】

    转自:https://blog.csdn.net/wangpengqi/article/details/9821231 rpm -ql libcap-2.16-5.2.el6.i686  /lib/l ...

  3. Linux的capability深入分析(1)【转】

    转自:https://blog.csdn.net/wangpengqi/article/details/9821227 一)概述: )从2.1版开始,Linux内核有了能力(capability)的概 ...

  4. (转) Linux的capability深入分析(2)

    一)capability的工具介绍   在我们的试验环境是RHEL6,libcap-2.16软件包中包含了相关的capability设置及查看工作,如下:   rpm -ql libcap-2.16- ...

  5. Linux select 机制深入分析

    Linux select 机制深入分析            作为IO复用的实现方式.select是提高了抽象和batch处理的级别,不是传统方式那样堵塞在真正IO读写的系统调用上.而是堵塞在sele ...

  6. 转载:linux capability深入分析

    转至http://www.cnblogs.com/iamfy/archive/2012/09/20/2694977.html 一)概述:  1)从2.1版开始,Linux内核有了能力(capabili ...

  7. Linux能力(capability)机制的继承

    1.Linux能力机制概述 在以往的UNIX系统上,为了做进程的权限检查,把进程分为两类:特权进程(有效用户ID是0)和非特权进程(有效用户ID是非0).特权进程可以通过内核所有的权限检查,而非特权进 ...

  8. 给linux安全模块LSM添加可链式调用模块(一)

    前些日子接了个外包的活,了解了一下Linux安全模块,发现了安全模块中的一些问题. 关于linux安全模块LSM在此就不多说了,大家google下就明白了. 这里主要介绍的是如何修改这个模块,使它可链 ...

  9. Linux VFS机制简析(一)

    Linux VFS机制简析(一) 本文主要基于Linux内核文档,简单分析Linux VFS机制,以期对编写新的内核文件系统(通常是给分布式文件系统编写内核客户端)的场景有所帮助. 个人渊源 切入正文 ...

随机推荐

  1. cf509A Maximum in Table

    A. Maximum in Table time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  2. 转自http://blog.sina.com.cn/daylive——C++ STL set&multiset

    C++ STL set和multiset的使用 1,set的含义是集合,它是一个有序的容器,里面的元素都是排序好的,支持插入,删除,查找等操作,就  像一个集合一样.所有的操作的都是严格在logn时间 ...

  3. 如何修改Oracle用户密码

    大家如果不知道oracle数据库的密码的话,我们可以通过简单的命令行把密码进行修改. 1.打开cmd 2 在弹出的命令提示窗口输入 set oracle_sid=数据库名称(实例),回车.例如数据库名 ...

  4. Servlet的init()方法如何才会在服务器启动时执行

    如果要想让 servlet 的 init () 方法在服务器启动 时就被执行,则需要在 web.xml 中相应的 servlet 下配置 <servlet > <servlet -n ...

  5. [Python]round四舍五入精度缺失的解决

    环境: os: win7 64bit python:2.7.5  32bit 对python四舍五入的解决方案 现象: 一般的四舍五入操作都是使用内置的round方法   In [14]: round ...

  6. POJ 2886 Who Gets the Most Candies? 线段树。。还有方向感

    这道题不仅仅是在考察线段树,还他妹的在考察一个人的方向感.... 和线段树有关的那几个函数写了一遍就对了,连改都没改,一直在转圈的问题的出错.... 题意:从第K个同学开始,若K的数字为正 则往右转, ...

  7. Java继承与清理

    [程序实例] import java.util.*; class Characteristic { private String s; Characteristic(String s) { this. ...

  8. Redis环境搭建(Linux)

    1.简介       redis是一个开源的key-value数据库.它又经常被认为是一个数据结构服务器.因为它的value不仅包括基本的string类型还有 list,set ,sorted set ...

  9. javaCV:爱之初体验

    最近实验室有了新任务,要求使用java进行模式识别,在具体点就是人脸识别.精确的边缘检测. 第一个问题便是环境配置,搭建工作台.(其实也不是什么难事,但是本人虽然从事较多的java开发,但很少接触模式 ...

  10. SQlserver表连接

    连接是两元运算,可以对两个或多个表进行查询,结果通常是含有参加连接运算的两个表或多个表的指定列的表. 在T-SQL中,连接查询有两类: 第一类:符合SQL标准的连接谓词表示形式: 第二类:T-SQL扩 ...