Linux下acl库的使用

为什么要使用ACL

lw@lw:~$ ls -l 14.txt
-rw-rw-r-- 1 lw lw 0 9月 18 09:27 14.txt

在这里说明了对于14.txt这个文件lw用户(由于是file owner)拥有read & write权限. 所有属于lw 组的用户(group)拥有read & write 权限. 其他任何用户(other)对于文件只有读权限。

如果我们现在希望testuser这个用户也可以对14.txt文件进行读操作,有以下几种操作 (这里假设testuser不属于lw group)

1、给other增加读权限,这样testuser会被归为other类别,所以他就拥有读权限。
2、将testuser加入到lw group,那么testuser会被归为lw group类别,那么他就拥有读写的权限。
3、设置sudo,使testuser能够以lw的身份对14.txt进行操作,从而获得读写权限。

第一种做法的问题在于所有用户都将对14.txt拥有读写操作,显然这种做法不可取。
第二种做法的问题在于testuser被赋予了过多的权限.所有属于lw组的文件,testuser都可以拥有其等同的权限了。
第三种做法虽然可以达到只限定john用户一人拥有对14.txt文件的读写权限.但是需要对sudoers文件进行严格的格式控制. 而且当文件数量和用户很多的时候,这种方法就相当地不灵活了。
看来好像都没有一个很好的解决方案. 其实问题就出在Linux 文件权限里面,对于other的定义过于广泛,以至于很难把权限限定于一个不属于file owner和group的用户身上. 那么Access Control List (ACL)就是用来帮助我们解决这个问题的。

简单地来说ACL就是可以设置特定用户或者用户组对于一个文件/文件夹的操作权限. 需要掌握的命令也只有三个: getfacl, setfacl, chacl

ACL的定义

下面来看一个定义好了的acl文件:

lw@lw:~$ getfacl 11.txt 
# file: 11.txt
# owner: lw
# group: lw
user::rw-
user:testuser:r--
group::rw-
group:testuser:r--
group:testuser2:--x
mask::rwx
other::-w-

前面三个以#开头的定义了文件名,文件所有者和文件拥有组, 这些信息没有太大的作用,我们可以用 --omit-header来省略掉
user::rw- 定义了ACL_USER_OBJ,说明file owner拥有读和写的权限
user:testuser:r-- 定义了ACL_USER,这样用户testuser就拥有了对文件的读权限,实现了我们一开始要达到的目的
group::rw- 定义了ACL_GROUP_OBJ,说明文件的group拥有read和write 权限
group:testuser:r-- 定义了ACL_GROUP,使得testuser组拥有了对文件的read权限
group:testuser2:–x定义了ACL_GROUP,使得testuser2组拥有了对文件的execute权限
mask::rw- 定义了ACL_MASK的权限为read and write
other::-w- 定义了ACL_OTHER,使得其他用户拥有write权限

设置文件ACL

首先我们还是要讲一下设置ACL文件的格式. 从上面的例子中我们可以看到每一个Access Entry都是由三个被:号分隔开的字段所组成. 第一个就是Entry tag type

user 对应了ACL_USER_OBJ和ACL_USER
group 对应了ACL_GROUP_OBJ和ACL_GROUP
mask 对应了ACL_MASK
other 对应了ACL_OTHER

第二个字段称之为qualifier.也就是上面例子中的testuser和testuser2组.它定义了特定用户和用户组对于文件的权限.这里我们也可以发现只有user和group才有qualifier,其他的都为空

第三个字段就是我们熟悉的权限了. 它和Linux的权限一样定义,这里就不多讲了

下面我们就来看一下怎么设置14.txt这个文件的ACL让它来达到我们上面的要求

一开始文件没有ACL的额外属性

lw@lw:~$ ls -l 14.txt
-rw-rw-r-- 1 lw lw 0 9月 18 09:27 14.txt

lw@lw:~$ getfacl 14.txt
# file: 14.txt
# owner: lw
# group: lw
user::rw-
group::rw-
other::r--
lw@lw:~$ getfacl -c 14.txt
user::rw-
group::rw-
other::r--

我们先让用户testuser3拥有对14.txt文件的读写权限

lw@lw:~$ setfacl -m u:testuser3:rw 14.txt
lw@lw:~$ getfacl -c 14.txt
user::rw-
user:testuser3:rw-
group::rw-
mask::rw-
other::r--

这时我们就可以看到testuser3用户在ACL里面已经拥有了对文件的读写权限. 这个时候如果我们查看一下linux的权限我们还会发现一个不一样的地方

lw@lw:~$ ls -l 14.txt
-rw-rw-r–+ 1 lw lw 0 9月 18 09:27 14.txt

在文件权限的最后多了一个+号。 当任何一个文件拥有了ACL_USER或者ACL_GROUP的值以后我们就可以称它为ACL文件。这个+号就是用来提示我们的,我们还可以发现当一个文件拥有了ACL_USER或者ACL_GROUP的值时ACL_MASK同时也会被定义

接下来我们来设置testuser3组拥有read 权限

lw@lw:~$ setfacl -m g:testuser3:r 14.txt
lw@lw:~$ getfacl -c 14.txt
user::rw-
user:testuser3:rw-
group::rw-
group:testuser3:r--
mask::rw-
other::r--

lw@lw:~$ ll 14.txt
-rw-rw-r–+ 1 lw lw 0 9月 18 09:27 14.txt

代码:

[root@zyq-server data]# ll test.sh
-rwxrw-r-- 1 root family 0 12-27 23:04 test.sh

这里说明test.sh文件只有file owner: root拥有read, write, execute/search 权限. Family组只有读和写的权限。现在我们想让用户zyq也对test.sh具有和root一样的权限。

代码:

[root@zyq-server data]# setfacl -m u:zyq:rwx test.sh
[root@zyq-server data]# getfacl -c test.sh
user::rwx
user:zyq:rwx
group::rw-
mask::rwx
other::r–

这里我们看到zyq已经拥有了rwx的权限.。mask值也被设定为rwx。那是因为它规定了ACL_USER, ACL_GROUP和ACL_GROUP_OBJ的最大值

现在我们再来看test.sh的Linux 权限, 它已经变成了

代码:

[root@zyq-server data]# ll test.sh
-rwxrwxr–+ 1 root family 0 12-27 23:04 test.sh

那么如果现在family组的用户想要执行test.sh的程序会发生什么情况呢? 它会被权限 deny。原因在于实际上family组的用户只有读和写的权限.这里当中显示的rwx是ACL_MASK的值而不是group的权限

所以从这里我们就可以知道,如果一个文件后面有+标记,我们都需要用getfacl来确认它的权限,以免发生混淆

下面我们再来继续看一个例子
假如现在我们设置test.sh的mask为read only,那么family组的用户还会有write 权限吗?

[root@zyq-server data]# setfacl -m mask::r test.sh
[root@zyq-server data]# getfacl -c test.sh
user::rwx
user:zyq:rwx #effective:r–
group::rw- #effective:r–
mask::r–
other::r–

这时候我们可以看到ACL_USER和ACL_GROUP_OBJ旁边多了个#effective:r–, 这是什么意思呢?
让我们来回顾一下ACL_MASK的定义。它规定了ACL_USER,ACL_GROUP_OBJ和ACL_GROUP的最大权限。那么在我们这个例子中他们的最大权限也就是read only。虽然我们这里给ACL_USER和ACL_GROUP_OBJ设置了其他权限,但是他们真正有效果的只有read权限。
这时我们再来查看test.sh的Linux 文件权限时它的group 权限也会显示其mask的值(i.e. r–)

[root@zyq-server data]# ll test.sh
-rwxr–r–+ 1 root family 0 12-27 23:04 test.sh

以上部分摘抄自:https://blog.csdn.net/weixin_41949714/article/details/81511451

下部分是自己编写的函数

函数编写(使用ACL库中的接口)

调用acl库, 封装一个函数。 判断用户是否具有文件的特定权限。 传入参数为用户uid、文件路径、权限(读、写、执行), 返回值为bool型。

#include <acl-lib/acl/lib_acl.h>//acl c库
#include <acl-lib/acl_cpp/lib_acl.hpp>//acl c++库

#include <acl/libacl.h>
#include <sys/acl.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <string>

#include <iostream>
using namespace std;

typedef unsigned int permset_t;

struct __acl_permset_ext {
	permset_t		s_perm;
} *permset;

typedef struct LNode{
    int elem;//代表数据域
    struct LNode * next;//代表指针域
}LNode,*LinkList;

int status_r, status_w, status_x;

int Listlength(LinkList L)
{
    int num = 0;
    LinkList p;
    p=L->next;
    while(p)
    {
        num++;
        p=p->next;
    }
    return num;
}


bool is_flag(char permission, acl_permset_t permset)
{
    bool type_flag = false;
    
    cout << "%$%$%$%$%$%$%$%$    " << permset->s_perm << "    %$%$%$%$%$%$%$%$" << endl;

    status_r = acl_get_perm(permset,ACL_READ);
    status_w = acl_get_perm(permset,ACL_WRITE);
    status_x = acl_get_perm(permset,ACL_EXECUTE);

    cout << "status_r: " << status_r << "\tstatus_w: " << status_w << "\tstatus_x: " << status_x << endl;
    
    if((permission == 'r') && (status_r == 1))
        type_flag= true;
    if((permission == 'w') && (status_w == 1))
        type_flag= true;
    if((permission == 'x') && (status_x == 1))
        type_flag= true;
    return type_flag;
}

/*
 * 获取用户所在组
 * 如果是域用户,可以获取到域用户所属的域POSIX组
*/
LinkList get_user_groups(const char* username, gid_t gid)
{
    int ngroups = 0 ,j = 0;
    LinkList L = (LinkList)malloc(sizeof(LNode));
    struct LNode *s,*r = L;
    getgrouplist(username,gid,NULL,&ngroups);       // 第一次调用获取组数量
    gid_t *groups = (gid_t*)malloc(sizeof (gid_t)*ngroups);
    getgrouplist(username,gid,groups,&ngroups);     // 第二次调用获取用户所属组的gid
    for(int i=0;i<ngroups;i++)
    {
        struct group *gr;
        gr = getgrgid(groups[i]);       // 基于gid获取组结构体,取得组名
        if(gr)
        {
            // data[j++] = gr->gr_gid;     //此处越界如何处理?  ly
            s = (LNode *)malloc(sizeof(LNode));
            s->elem = gr->gr_gid;
            r->next = s;
            r = s;
            printf("%d ",gr->gr_gid);
        }
    }
    r->next = NULL;//尾节点置空
    printf("\n");
    free(groups);
    // return ngroups;
    return L;
}


//判断用户是否具有文件的特定权限。
bool is_user_specific_permissions(uid_t uid, const char *path, char permission){
    if(access(path, F_OK) != -1)//该文件存在
    {
        acl_t acl;
        acl = acl_get_file(path, ACL_TYPE_ACCESS);
  
        char *str;
        ssize_t len;
        //将内存acl转换为字符串,成功返回字符串长度
        str = acl_to_text(acl,&len);
        string acl_str = str;
        cout << "str为:\n" << str << endl;
		acl_free(str);

   
        acl_entry_t entry;
        
        //entry_id的值可以为ACL_FIRST_ENTRY或者ACL_NEXT_ENTRY,该函数可用于遍历acl的所有记录
        acl_get_entry(acl,ACL_FIRST_ENTRY,&entry);
        
        acl_tag_t tag_type;
        acl_get_tag_type(entry,&tag_type);

        cout << "acl_tag_t: " << tag_type << endl;

        //获取本机用户的权限
        // acl_permset_type permset;
        acl_get_permset(entry,&permset);
        cout << "permset: " << permset->s_perm << endl;

        struct stat st;
        memset(&st, 0, sizeof(st));
        stat(path, &st);

		cout << "文件属主uid: " << st.st_uid << endl;
        cout << "文件属组gid: " << st.st_gid << endl;

        struct passwd *pw_ptr;
        char *pw_name= NULL;
        static char numstr[10];
        
        //通过uid获取其所属的gid
        if((pw_ptr= getpwuid(uid))==NULL)
        {
            sprintf(numstr,"%d",uid);
            return false;
        }
        else
        {
            cout << "输入uid用户的gid:" << pw_ptr->pw_gid << "\t输入uid用户的名字:" << pw_ptr->pw_name << endl;
            // pw_name = strdup(pw_ptr->pw_name);
        }
        
        LinkList L;
        LNode *node;
        printf("%d用户所属用户组为:",uid);
        L = get_user_groups(pw_ptr->pw_name, pw_ptr->pw_gid);
        int length = Listlength(L);
        node = L->next;

        uid_t *qual;
        gid_t *gual;
        string::size_type mask_symbol;
        char mask_permission, status_permission;
        bool user_obj_flag=false,user_flag=false,is_user_flag = false;
        bool group_obj_flag=false, group_flag=false, mask_flag = false, other_flag=false;
        bool acl_group_obj_flag = false, acl_group_flag = false,mask_symbol_flag = false;

        if (uid == st.st_uid)
        {
            user_obj_flag = is_flag(permission, permset);
            return user_obj_flag;    
        }
        
        
        while (true)
        {
            acl_get_entry(acl,ACL_NEXT_ENTRY,&entry);
            acl_get_tag_type(entry,&tag_type);
            cout << "acl_tag_t: " << tag_type << endl;

            acl_get_permset(entry,&permset);
            cout << "permset: " << permset->s_perm << endl;

            switch (tag_type)
            {
                case ACL_USER:
                    qual = (uid_t *)acl_get_qualifier(entry);
                    cout << "uid_qual:" << *qual << endl;
                    if (uid == *qual)
                    {
                        user_flag = is_flag(permission, permset);
                    }
                    
                    acl_free(qual);//释放内存
                    cout << "pw_ptr->pw_name: " << pw_ptr->pw_name << endl;
                    //用户不在ACL_USER中
                    if((acl_str.find(pw_ptr->pw_name)) == string::npos)
                    {
                        break;
                    }

                    mask_symbol = acl_str.find("effective");
                    if (mask_symbol != string::npos)//effective不为空
                    {
                        mask_symbol_flag = true;
                    }
                    else//effective为空且用户存在ACL_USER中
                        is_user_flag = true;
                    break;
                case ACL_GROUP_OBJ:
                    if(mask_symbol_flag == false)//effective为空
                    {
                        if(node == NULL)
                            cout << "单链表中没有数据" << endl;
                        while(node != NULL)
                        {
                            if (node->elem == st.st_gid)
                            {
                                cout << "++++++++" << node->elem << "++++++++" << endl;
                                acl_group_obj_flag = true;
                                group_obj_flag = is_flag(permission, permset);
                                cout << "$$$$$$" << group_obj_flag << "$$$$$$" << endl;
                                node = node->next;
                                if (group_obj_flag)
                                {
                                    free(node);
                                    free(L);
                                    return true;
                                }
                                break;
                            }
                            //为了解决链表中只有一个数据,指针往下一位指后,再次循环上面的if比较失败的情况
                            // if (length == 1)
                            //     break;
                            node = node->next;
                        }
                        node = L->next;
                    }
                    break;
                case ACL_GROUP:
                    gual = (gid_t *)acl_get_qualifier(entry);
                    cout << "gual:" << *gual << endl;
                                       
                    if (mask_symbol_flag == false)//effective为空
                    {
                        if(node == NULL)
                            cout << "单链表中没有数据" << endl;
                        while(node != NULL)
                        {
                            cout << "node->elem: " << node->elem << endl;
                            if (node->elem == *gual)//用户存在于用户组中
                            {
                                cout << "-------" << node->elem << "-------" << endl;
                                acl_group_flag = true;
                                group_flag = is_flag(permission, permset);
                                cout << "######" << group_flag << "######" << endl;
                                node = node->next;
                                if (group_flag)
                                {
                                    free(node);
                                    free(L);
                                    return true;
                                }
                                break;
                            }
                            //为了解决链表中只有一个数据,指针往下一位指后,再次循环上面的if比较失败的情况
                            // if (length == 1)
                            //     break;
                            node = node->next;
                        }
                        node = L->next;
                    }

                    acl_free(gual);//释放内存
                    break;
                case ACL_MASK:
                    if (mask_symbol_flag == true)
                    {
                        if (permset->s_perm == 4)
                            mask_permission = 'r';
                        else if (permset->s_perm == 2)
                            mask_permission = 'w';
                        else if (permset->s_perm == 1)
                            mask_permission = 'x';

                        if(status_r == 1)
                            status_permission = 'r';
                        else if(status_w == 1)
                            status_permission = 'w';
                        else if(status_x == 1)
                            status_permission = 'x';

                        cout << "status_permission: " << status_permission << "\tmask_permission: " << mask_permission << endl;
                        
                        if (status_permission == mask_permission)
                            return true;
                        else
                            return false;
                    }
                    break;
                case ACL_OTHER:
                    //用户
                    if(user_flag == false && is_user_flag == true)
                        return false;
                        
                        
                    if(user_flag == true && is_user_flag == true)
                        return true;
                        
                    //用户以属组权限为准
                    if(group_obj_flag == false && acl_group_obj_flag == true)
                    {
                        free(node);
                        free(L);
                        return false;    
                    }                       
                    if(group_flag == false && acl_group_flag == true)
                    {
                        free(node);
                        free(L);
                        return false;
                    }

                    //用户即不是属主也不是属组成员,effective权限为空
                    other_flag = is_flag(permission, permset);
                    return other_flag;
                    break;
                default:
                    break;
            }
        
        }
        
        
    }
    else{
        std::cout << "The file is not exist" << endl;
        return false;
    }

    return false;
}



int main() {
    bool flag = is_user_specific_permissions(1003, "/home/lw/13.txt", 'r');
    cout << "flag为:" << flag << endl;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值