概述

一个 ACL 由一系列 ACL 记录(简称ACE)组成,其中每条记录都针对单个用户或用户组定义了对文件的访问权限。
ACL记录,每条 ACE 都由3部分组成:

  • 标记类型:表示该记录作用于一个用户、组,还是其他类别的用户。该值为一系列ACL_开头的常量
  • 标记限定符(可选项)标识特定的用户或组(亦即,某个用户ID或组ID)
  • 权限集合:本字段包含所授予的权限信息

Linux系统是以系统扩展属性来实现ACL的。

ACL 权限检查算法

其流程如下:

  1. 若进程具有特权,则拥有所有访问权限
  2. 若某一进程的有效用户ID匹配文件的属主(用户ID),则授予该进程标记类型为ACL_USER_OBJ的 ACE 所指定的权限
  3. 若进程的有效用户ID与某一ACL_USER类型记录的标记限定符想匹配,则授予该进程此记录所指定的权限与 ACL_MASK 型记录值相与(&)的结果
  4. 若进程的组ID之一匹配于文件组,或者任一ACL_GROUP型记录的标记限定符,则会依次进行如下检查,直至发现匹配项:

    1. 若进程的组ID之一匹配于文件组
    2. 若进程的组ID之一匹配于该文件所辖ACL_GROUP型 ACE 的标记限定符
  5. 否则,将以ACL_OTHER型 ACE 所记录的权限授予进程

ACL 的长、短文本格式

执行setfaclgetfacl命令,或是使用某些 ACL 库函数操纵 ACL 时,需指明 ACE 的文本表现形式。ACE 的文本格式有两种:

  • 长文本格式的 ACL:每行都包含一条 ACE,还可以包含注释,直至行尾结束
  • 短文本格式的 ACL:包含一系列以,分隔的 ACE

无论是上述哪种格式,每条 ACE 都由以:分隔的三部分组成

tag-type:[tag-qualifier]:permissions

ACL_mask 型 ACE 和 ACL 组分类

对于ACL_MASK标记类型的 ACE,其作用在于是所谓“组分类”中 ACE 所能授予权限的上限。组分类是指在 ACL 中,由所有标记类型为ACL_USERACL_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_api.png

将文件到 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 文件权限模型的扩展,籍此可在每用户或每组的基础上来控制对文件的访问。

标签: Linux, C/C++, TLPI

添加新评论