linux权限检查机制

1.文件权限 

static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask)
  {
      if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
          if (likely(mnt && inode->i_op->permission2))
              return inode->i_op->permission2(mnt, inode, mask);
          if (likely(inode->i_op->permission))
              return inode->i_op->permission(inode, mask);
  
          /* This gets set once for the inode lifetime */
          spin_lock(&inode->i_lock);
          inode->i_opflags |= IOP_FASTPERM;
          spin_unlock(&inode->i_lock);
      }    
      return generic_permission(inode, mask);                                                                                                                            
  }

int generic_permission(struct inode *inode, int mask)
  {
      int ret; 
  
      /*   
       * Do the basic permission checks.
       */
      ret = acl_permission_check(inode, mask);   //基本权限检查
      if (ret != -EACCES)
          return ret; 
  
      if (S_ISDIR(inode->i_mode)) {
          /* DACs are overridable for directories */
          if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))    //CAP_DAC_OVERRIDE是CAP_DAC_READ_SEARCH的超集,不管读写都可以
              return 0;
          if (!(mask & MAY_WRITE))
              if (capable_wrt_inode_uidgid(inode,
                               CAP_DAC_READ_SEARCH))   //不是写
                  return 0;
          return -EACCES;
      }    
      /*   
       * Read/write DACs are always overridable.
       * Executable DACs are overridable when there is
       * at least one exec bit set.
       */
      if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))//如果是执行,则有些不同,至少要有一个执行权限
          if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
              return 0;
  
      /*   
       * Searching includes executable on directories, else just read.
       */
      mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
      if (mask == MAY_READ)
          if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH))
              return 0;                                                                                                                                                  
  
      return -EACCES;
  }

static int acl_permission_check(struct inode *inode, int mask)
  {
      unsigned int mode = inode->i_mode;
  
      if (likely(uid_eq(current_fsuid(), inode->i_uid)))  //同一个用户, 使用的是进程的fsuid
          mode >>= 6;
      else {
          if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {    //注意看acl是如何工作的
              int error = check_acl(inode, mask); 
              if (error != -EAGAIN)
                  return error;
          }
  
          if (in_group_p(inode->i_gid))  //同一组
              mode >>= 3;

            //否则是other,不用移位
      }
  
      /*
       * If the DACs are ok we don't need any capability check.
       */
      if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
          return 0;
      return -EACCES;
  }

2.能力的检查

 bool ns_capable(struct user_namespace *ns, int cap)
  {   
      return ns_capable_common(ns, cap, true);                                                                                                                           
  }

  static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit)                                                                                          
  {
      int capable;
  
      if (unlikely(!cap_valid(cap))) {
          pr_crit("capable() called with invalid cap=%u\n", cap);
          BUG();
      }
  
      capable = audit ? security_capable(current_cred(), ns, cap) :
                security_capable_noaudit(current_cred(), ns, cap);
      if (capable == 0) {
          current->flags |= PF_SUPERPRIV;
          return true;
      }
      return false;
  }

 struct security_hook_list capability_hooks[] = {
      LSM_HOOK_INIT(capable, cap_capable),

.....}

*int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,                                                                                               
          int cap, int audit)
  {
    int ret = __cap_capable(cred, targ_ns, cap, audit);
  }

int __cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
          int cap, int audit)
  {
      struct user_namespace *ns = targ_ns;
  
      /* See if cred has the capability in the target user namespace
       * by examining the target user namespace and all of the target
       * user namespace's parents.
       */
      for (;;) {
          /* Do we have the necessary capabilities? */
          if (ns == cred->user_ns)
              return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
                                                                                                                                                                         
          /* Have we tried all of the parent namespaces? */
          if (ns == &init_user_ns)
              return -EPERM;
  
          /* 
           * The owner of the user namespace in the parent of the
           * user namespace has all caps.
           */
         if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
              return 0;
  
          /* 
           * If you have a capability in a parent user ns, then you have
           * it over all children user namespaces as well.
           */ 
         ns = ns->parent;
      }   
      
      /* We never get here */
  } 

如果是root,则默认具有所有能力,即CapInh,CapPrm,CapEff,CapBnd所有bit都是1,包括CAP_DAC_OVERRIDE,所有root可以操作任何文件,通过setuid()和setgid()可以切换到非root用户,此时CapInh,CapPrm,CapEff都变为0,CapBnd维持不变,即默认非root用户失去所有的能力。

通过设置程序文件的能力bit,可以给普通用户授予某项能力

setcap  cap_net_raw=eip /bin/ping

此时任何用户都可以ping,这是比suid更安全的一种做法,只授予需要的权限,而不像suid授予所有的权限(everything or nothing),文件能力存储在文件系统的xattr中,并且挂载时不能指定nosuid选项,否则文件能力失效。

如果文件设置了能力,程序能力具体的规则如下:

我们使用P代表执行exec前的capabilities,P’代表执行exec后的capabilities,F代表exec执行的文件的capabilities。那么:

P’(Permitted) = (P(Inheritable) & F(Inheritable)) | (F(Permitted) & cap_bset)

P’(Effective) = F(Effective) ? P’(Permitted) : 0

P’(Inheritable) = P(Inheritable)

扫描二维码关注公众号,回复: 8725998 查看本文章

其中的cap_bset是capability bounding set。通过与文件的Permitted集合计算交集,可进一步限制某些capabilities的获取,从而降低了风险

对于文件没有设置能力,则按如下规则:

P’(Permitted) =P(Inheritable)

P’(Effective)=P(Effective)

P’(Inheritable) = P(Inheritable)

如果某个程序文件具有cap_setuid,cap_setgid的能力,则非root用户可以通过setuid()和setgid()可以切换到root用户,此时:

CapInh,CapPrm,CapEff=CapBnd

发布了85 篇原创文章 · 获赞 26 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/whuzm08/article/details/84451650