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;
}