zoukankan      html  css  js  c++  java
  • Linux权限管理(一)—打开权限

    一、打开权限
    这里其实比较感兴趣的是文件夹的权限,假设对于root用户的一个文件夹,或者另一个不允许其它用户访问的文件夹,如果用户访问这个路径是否可以访问文件夹下的文件?
    简单的模型是这样的
    [root@Harry ~]# ll /
    drwxr-x--x.   2 root root  4096 2012-02-14 21:32 priotest
    [root@Harry ~]# ll /priotest/
    total 4
    -rwx--xr-x. 1 root root 12 2012-02-14 21:34 tsecer.txt
    这里的情况是文件夹/priotest文件夹对其他用户设置的是执行权限而没有打开权限,但是该文件夹下的实体文件却是有读出权限的,那么此时普通用户是否能够读取该文件的内容呢?那么此时普通用户是否可以查看该文件的内容呢?
    二、内核实现
    我们看一下打开文件内核的目录打开执行的流程中权限的检测为
    do_path_lookup
    使用的权限判断为
            retval = file_permission(file, MAY_EXEC);
    __link_path_walk中判断为
    exec_permission_lite(inode, nd)
    也就是文件夹打开的权限判断都是使用了文件夹的EXEC权限而不是打开权限。事实上,切换到一个普通用户,然后使用cat命令同样可以查看这个文件的内容:
    [tsecer@Harry priotest]$ cat /priotest/tsecer.txt
    hello world
    tsecer
    三、文件夹的打开权限是干什么的
    这个输出是我在机器上执行strace命令的一些输出
    [tsecer@Harry priotest]$ strace ls /
    ……
    open("/", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_CLOEXEC) = 3
    fcntl64(3, F_GETFD)                     = 0x1 (flags FD_CLOEXEC)
    getdents64(3, /* 26 entries */, 32768)  = 696
    getdents64(3, /* 0 entries */, 32768)   = 0
    这里可以看到,其中对于根文件夹"/"是直接打开,然后读出目录下的所有文件内容的,所以对于文件夹的读权限和普通文件的读权限是一样的,最后就是文件的读权限判断在open_namei函数的最后,其执行的判断为
    error = may_open(nd, acc_mode, flag);
    这里判断的就是文件的打开权限判断,此处的判断最终是通过
    vfs_permission--->>>permission--->>generic_permission
    if (current->fsuid == inode->i_uid) 
            mode >>= 6;对于一个文件的访问权限设置,最高最高3bits为用户所有者权限,所以如果fsud和文件创建者id相同,则逻辑右移6bits
        else {
            if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
                int error = check_acl(inode, mask);
                if (error == -EACCES)
                    goto check_capabilities;
                else if (error != -EAGAIN)
                    return error;
            }

            if (in_group_p(inode->i_gid)) 如果当前进程和文件在同一用户组中,则右移3bits
                mode >>= 3;
        } 其它的使用的就是mode的最后3bits,作为其它用户的访问权限设置

        /*
         * If the DACs are ok we don't need any capability check.
         */
        if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))这里就是用文件的权限控制列表和对该文件要求的权限判断,如果满足,则返回零,表示进程可以打开该文件
            return 0;
    关于这个9bits控制模式,可以看一下chmod命令的说明,这里就不废话了。
    这也就是说,如果一个用户对文件夹没有打开权限,那么它不能通过ls来查看该文件夹下文件内容,进一步的一个简单推论就是在bash中,不同通过tab来自动补全文件夹下文件。
    四、超级用户如何超越这些判断
    超级用户可以绕过这些控制,假设一个极端的情况,一个超级用户自毁长城,将一个自己创建的文件访问属性设置为000,也就是任何人没有任何权限,那么这个文件是不是在系统中就长生不老了?简单测试了一下,不是。超级用户是如何超越这个检测的?
    其实同样是在generic_permission函数中实现的,上面的判断并不是文件的全部,而只是一部分,即使上面的
        if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
      不满足,还有绿色通道,接下来的就是对权限(能力)的判断,这相当于是root用户的后门:
     check_capabilities:
        /*
         * Read/write DACs are always overridable.
         * Executable DACs are overridable if at least one exec bit is set.
         */
        if (!(mask & MAY_EXEC) || 对于文件夹的打开,这里的第一个逻辑或就已经满足,这是通过文件夹权限检测的步骤。
            (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
            if (capable(CAP_DAC_OVERRIDE))
                return 0;

        /*
         * Searching includes executable on directories, else just read.
         */
        if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))这里对读权限的超越
            if (capable(CAP_DAC_READ_SEARCH))
                return 0;
    对于root用户,这些权限都是满足的:
    [root@Harry priotest]# cat /proc/self/status 
    Name:    cat
    State:    R (running)
    ……
    CapInh:    0000000000000000
    CapPrm:    ffffffffffffffff
    CapEff:    ffffffffffffffff
    CapBnd:    ffffffffffffffff
    可以看到,root用户具有所有的权限,其中的capable是使用了其中的CapEff列来判断权限的。
    五、open打开权限到ACCESS MODE的转换
    这是一个小问题,因为当用户态调用open的时候,如果是要求可读写状态打开一个文件,传入的参数是O_RDWR,但是在内核里搜索了这个字符串,没有发现对这个属性进行判断的地方。借助调试器,才看到这个转换是在一个不起眼并且比较诡异的方式实现的,在
    static struct file *do_filp_open(int dfd, const char *filename, int flags,
                     int mode)
        if ((namei_flags+1) & O_ACCMODE)
            namei_flags++;
    然后在
    int open_namei(int dfd, const char *pathname, int flag,
            int mode, struct nameidata *nd)
        acc_mode = ACC_MODE(flag);
    其中
    #define ACC_MODE(x) ("00040206"[(x)&O_ACCMODE])
    #define O_ACCMODE    00000003
    假设传入的属性为
    #define O_RDWR        00000002
    那么它将会在do_filp_open中转换为3,所以在接下来的ACC_MODE转换中转换为006,也就是3bits访问控制权限中的110,所以这个权限就要求文件有读、写(没有要求执行权限)。
    六、一个细节
    在generic_permission函数中,文件的访问使用的都是进程的fsuid 属性,而一个进程描述符中关于uid,有下面一些定义
        uid_t uid,euid,suid,fsuid;
    这些id的具体应用,在之后的描述中将会尝试逐步展开。

  • 相关阅读:
    mysql配置utf8_mb4
    Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by serv
    MongoDB自动删除过期数据--TTL索引
    正则小公举
    苹果手机弹起输入框将页面上的元素上移
    location的部分属性
    在ajax请求下的缓存机制
    苹果机的时间格式转换为时间搓
    $.extendGit 丢弃所有本地修改的方法
    调起微信扫一扫
  • 原文地址:https://www.cnblogs.com/tsecer/p/10486169.html
Copyright © 2011-2022 走看看