转载:linux capability深入分析

Posted fellow_jing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了转载:linux capability深入分析相关的知识,希望对你有一定的参考价值。

转至http://www.cnblogs.com/iamfy/archive/2012/09/20/2694977.html

一)概述:

 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:表示能够被当前进程执行的程序继承的能力.
 
#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); // resetting caps storage

  if (cap_set_proc(caps)) {
    perror("capset()");
    return EXIT_FAILURE;
  }
  listCaps();   
  cap_free(caps);
  return 0;}

 

编译:

gcc capsettest.c -o capsettest -lcap
 编译时找不到sys/capability.h 需要安装libcap(http://www.man7.org/linux/man-pages/man8/setcap.8.html)或者apt-get install libcap-dev
运行:
./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);
      cap_value_t capList[5] =
{ CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP } ;
  unsigned num_caps = 5;
      unsigned int i = 0;
      unsigned int capmask = 0;
      for (i = 0; i < num_caps; i++)
      {
    capmask |= CAP_TO_MASK(capList[i]);
      }
      memset (cap_data, 0, sizeof(*cap_data));
      capdata.effective = capmask;
  capdata.permitted = capmask;
  capdata.inheritable = capmask;
  
   if (capset(cap_header, cap_data) < 0) {
         perror("Failed capget");
         exit(1);
     }
}

gcc capget0.c -o capget0 -lcap

普通用户:
./capget0 
Cap data 0x0, 0x0, 0x0
 
超级用户:
/home/test/capget0 
Cap data 0xffffffff, 0xffffffff, 0x0
 
这也说明了默认情况下,root运行的进程是什么权限都有,而普通用户则什么权限都没有.

 

 

以上是关于转载:linux capability深入分析的主要内容,如果未能解决你的问题,请参考以下文章

linux内核capable源代码分析

详述Linux系统中Apache网页深入优化

转载深入分析 ThreadLocal 内存泄漏问题

JS的引擎深入分析-转载

Linux Android 多点触摸协议 原文出自比特网,转载请保留原文链接:http://soft.chinabyte.com/os/71/12306571.shtml

转载:Linux操作系统(1.3)《深入理解Nginx》