【TLPI读书笔记】 十七、访问控制列表
概述
一个 ACL 由一系列 ACL 记录(简称ACE)组成,其中每条记录都针对单个用户或用户组定义了对文件的访问权限。
ACL记录,每条 ACE 都由3部分组成:
- 标记类型:表示该记录作用于一个用户、组,还是其他类别的用户。该值为一系列
ACL_
开头的常量 - 标记限定符(可选项)标识特定的用户或组(亦即,某个用户ID或组ID)
- 权限集合:本字段包含所授予的权限信息
Linux系统是以系统扩展属性来实现ACL的。
ACL 权限检查算法
其流程如下:
- 若进程具有特权,则拥有所有访问权限
- 若某一进程的有效用户ID匹配文件的属主(用户ID),则授予该进程标记类型为
ACL_USER_OBJ
的 ACE 所指定的权限 - 若进程的有效用户ID与某一
ACL_USER
类型记录的标记限定符想匹配,则授予该进程此记录所指定的权限与ACL_MASK
型记录值相与(&
)的结果 若进程的组ID之一匹配于文件组,或者任一
ACL_GROUP
型记录的标记限定符,则会依次进行如下检查,直至发现匹配项:- 若进程的组ID之一匹配于文件组
- 若进程的组ID之一匹配于该文件所辖
ACL_GROUP
型 ACE 的标记限定符
- 否则,将以
ACL_OTHER
型 ACE 所记录的权限授予进程
ACL 的长、短文本格式
执行setfacl
和getfacl
命令,或是使用某些 ACL 库函数操纵 ACL 时,需指明 ACE 的文本表现形式。ACE 的文本格式有两种:
- 长文本格式的 ACL:每行都包含一条 ACE,还可以包含注释,直至行尾结束
- 短文本格式的 ACL:包含一系列以
,
分隔的 ACE
无论是上述哪种格式,每条 ACE 都由以:
分隔的三部分组成
tag-type:[tag-qualifier]:permissions
ACL_mask 型 ACE 和 ACL 组分类
对于ACL_MASK
标记类型的 ACE,其作用在于是所谓“组分类”中 ACE 所能授予权限的上限。组分类是指在 ACL 中,由所有标记类型为ACL_USER
、ACL_GROUP
以及ACL_GROUP_OBJ
的 ACE 所组成的集合。
提供标记类型为ACL_MASK
的 ACE,其目的在于即使运行并无 ACL 概念的应用程序,也能保障其行为的一致性。
getfacl 和 setfacl 命令
在shell中运行getfacl
命令,可查看到应用于文件的 ACL;setfacl
命令可用于修改文件的 ACL。
默认 ACL 与文件创建
上述所讲,都是介绍的访问型 ACL。顾名思义即:对文件访问的权限检查将使用访问型(access) ACL 来判定。针对目录还可创建第二种 ACL:默认型(default) ACL。
访问目录时,默认型 ACL 并不参与判定所授予的权限。相反,默认型 ACL 的存在与否决定了在目录下所创建文件或子目录的 ACL 和权限。
ACL 在实现方面的限制
各类文件系统都对一 ACL 中所含记录的条数有所限制。
通常的做法是:在系统组文件中定义适当的组,并在 ACL 中运用起来,从而将文件 ACL 的记录条数保持在一个较低的合理水平。
ACL API
POSIX.1e 标准草案围绕着操纵 ACL 定义了大量函数和数据结构。程序要使用 ACL API,就应该包含<sys/acl.h>
;如果还用到了 POSIX.1e 标准草案中到各种Linux扩展,程序还需要包含<acl/libacl.h>
,编译此类程序需要带-lacl
选项以与libacl
库链接。
将文件到 ACL 读入内存
acl_get_file()
函数可用来获取文件到 ACL 副本
acl_t acl;
acl = acl_get_file(pathname, type);
从内存 ACL 中获取记录
acl_get_entry()
函数会返回一句柄,指向内存 ACL(由函数的 acl 参数指代)中的记录之一
acl_entry_t entry;
status = acl_get_entry(acl, entry_id, &entry);
获取并修改 ACL 记录中的属性
函数acl_get_tag_type()
和acl_set_tag_type()
可分别用来获取和修改(由 entry 参数所指定)ACL 记录中的标记类型
acl_tag_t tag_type;
status = acl_get_tag_type(entry, &tag_type);
status = acl_set_tag_type(entry, tag_type);
函数acl_get_qualifier()
和acl_set_qualifier()
可分别用来获取和修改(由 entry 参数所指定)ACL 记录中的标记限定符
uid_t *qualp;
qualp = acl_get_qualifier(entry);
status = acl_set_qualifier(entry, qualp);
函数acl_get_permset()
和acl_set_permset()
可分别用来获取和修改(由 entry 参数所指定)ACE 中的权限集合
uid_t *qualp;
qualp = acl_get_qualifier(entry);
status = acl_set_qualifier(entry, qualp);
创建和删除 ACE
acl_create_entry()
函数用来在某一现有 ACL 中新建一条记录。该函数会将一个指代新建 ACE 的句柄返回到由其第二个参数所指定的内存位置
acl_entry_t entry;
status = acl_create_entry(&acl, &entry);
然后可利用先前介绍过的函数来设置该记录。acl_delete_entry()
函数用来从 ACL 中删除一条 ACE。
status = acl_delete_entry(acl, entry);
更新文件的 ACL
acl_set_file()
函数的作用与acl_get_file()
相反,将使用驻留于内存的 ACL 内容(由 acl 参数所指代)来更新磁盘上的 ACL
int status;
status = acl_set_file(pathname, type, acl);
ACL 在内存和文本格式之间的转换
acl_from_text()
函数可将包含文本格式 ACL(长短不拘)的字符串转换为内存 ACL,并返回一个句柄,用以在后续函数调用中指代该ACL。
acl = acl_from_text(acl_string);
acl_to_text()
则执行与上述函数相反的转换,并同时返回对应于 ACL(由 acl 参数指定)的长文本格式字符串
char *str;
ssize_t len;
str = acl_to_text(acl, &len);
API 中的其他函数
acl_calc_mask(&acl)
函数用来计算并设置内存 ACL(由 acl 参数指定)中 ACL_MASK 型记录的权限acl_delete_def_file(pathname)
函数用来删除目录acl_init(count)
函数用来新建一个空的 ACL 结构,其空间足以容纳由参数count
所指定的记录数acl_dup(acl)
函数用来为由acl
参数所指定的 ACL 创建副本,并以该副本的句柄作为返回值acl_free(handle)
函数用来释放由其他 ACL 函数所分配的内存
总结
学Linux的都知道ACL,但是最初都是通过命令行使用它,本章深入讲解了其内部实现原理和相关API函数。ACL是对传统 UNIX 文件权限模型的扩展,籍此可在每用户或每组的基础上来控制对文件的访问。