Linux支持POSIX ACL。Linux常规的file mode,支持user/group/other 3种权限控制。它的缺点,对于非user和group的other,权限都是一样的。通过ACL可以设置特定user或特定group的访问权限。在文件共享需要灵活设置权限时,ACL就派上了用场。
acl把文件和目录叫作object。acl有两种类型:access acl 和 default acl。
acess acl 用来控制 object的访问权限。
default acl 用来控制新创建的子object的初始access acl。因此只有目录才有default acl。
每个acl条目包括3个字段:
entry tag type
entry tag qualifier
permissions
tag type有6种:
ACL_USER_OBJ | 控制file owner的访问权限 |
ACL_USER | 控制其它用户的访问权限,用户id存储在qualifier |
ACL_GROUP_OBJ | 控制file group的访问权限 |
ACL_GROUP | 控制其它group的访问权限,group id存储在qualifier |
ACL_MASK | ACL_USER、ACL_GROUP_OBJ、ACL_GROUP 要和这个掩码与一下才是最后的访问权限 |
ACL_OTHER | 其他用户或组的访问权限 |
tag qualifier:
用来存储用户或者组id,只有ACL_USER和ACL_GROUP需要存储user id 和 group id。其它的type为空。
permissions:
和普通的文件权限一样,read/write/execute。
ACL_USER_OBJ/ACL_GROUP_OBJ/ACL_OTHER,只能有一个
ACL_USER可以有多个,但是user id不能重复。
ACL_GROUP有多个,但是group id不能重复。
如果有ACL_USER或者ACL_GROUP,那么必须有且仅有一个ACL_MASK,否则ACL_MASK是可选的。
1 如果进程的euid匹配object的owner
检查ACL_USER_OBJ确定是否授予权限。返回granted或者denied,并且不继续下面的流程。
2 如果进程的euid,匹配其中一个ACL_USER
那么检查ACL_USER和ACL_MASK,确定是否授权,并且不继续下面的流程。
3 如果进程的egid或者supplementary group ID,匹配file group或者任何一个ACL_GROUP,那么
那么检查ACL_MASK 和 (任何一个匹配的ACL_GROUP或者ACL_GROUP_OBJ),确定是否授权。
如果ACL_MASK没授权,那么就是拒绝。
如果ACL_MASK授权了,且匹配的任何一个GROUP授权,就授权。注意这里可能匹配多个group,只要一个授权就可以了。
4 检查ACL_OTHER是否授权。
5 拒绝。
如果开启了ACL,那么就不检查file mode里面的user/group/other权限了。
因为ACL里面的ACL_USER_OBJ/ACL_GROUP_OBJ/ACL_OTHER已经包含了这些权限。
ACL是file mode的超集,修改file mode的权限,也会同步修改ACL对应的条目,根据man 1 acl的描述:
The file owner, group, and other permissions always match the permissions of the corresponding ACL entry. Modification of the file permission bits results in the modification of the associated ACL entries, and modification of these ACL entries results in the modification of the file permission bits.
当使用creat()/mkdir()/mknod()/mkfifo()/open(),携带mode参数创建文件或目录时,
如果父目录有default acl。那么新建object的accesss acl继承自父目录的default acl,然后根据mode修改和文件属性对应的acl entry的权限,保证没有含有mode之外的权限。注意,这里umask不起作用了。
如果父目录没有default acl,那么子目录没有acl。按照普通的 mode & umask确定权限。
子目录继承父目录的default acl。
ACL条目的3个字段,用冒号分开:
type:id:perm
type | long | short |
ACL_USER、ACL_USER_OBJ | user | u |
ACL_GROUP、ACL_GROUP_OBJ | group | g |
ACL_MASK | mask | m |
ACL_OTHER | other | o |
ACL_USER和ACL_GROUP的id,是对应的用户id和组id,其它的type,此字段为空。
perm是权限:如,r-x
例子:
u:nas0:rx
用户nas0具有rx权限
如果一个object开启了ACL,那么使用ls的时候,权限后面会多一个+。比如:
$ ls -l abc
-rw-r-----+ 1 yuan yuan 0 Dec 24 17:42 abc
linux将acl存储在文件的xattr属性中:
default acl对应:system.posix_acl_default
access acl对应:system.posix_acl_access
存储为二进制格式,一个头 + 多个条目;
struct posix_acl_xattr_entry {
__le16 e_tag;
__le16 e_perm;
__le32 e_id;
};
struct posix_acl_xattr_header {
__le32 a_version;
};
两个头文件:
linux/posix_acl.h
linux/posix_acl_xattr.h
不使用libacl库,直接打印acl的c代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | $ cat dumpacl.c #include <stdio.h> #include <sys/xattr.h> #include <linux/posix_acl_xattr.h> #include <linux/posix_acl.h> #include <endian.h> static const char *acl_type( struct posix_acl_xattr_entry *entry) { int tag = le16toh(entry->e_tag); switch (tag) { case ACL_USER_OBJ: case ACL_USER: return "u" ; case ACL_GROUP_OBJ: case ACL_GROUP: return "g" ; case ACL_MASK: return "m" ; case ACL_OTHER: return "o" ; } return "" ; } static const char *acl_id( struct posix_acl_xattr_entry *entry) { unsigned id = le32toh(entry->e_id); static char str[32]; if (id == -1) return "" ; snprintf(str, sizeof (str), "%u" , id); return str; } static const char *acl_perm( struct posix_acl_xattr_entry *entry) { static char str[32]; int perm = le16toh(entry->e_perm); int n = 0; if (perm & ACL_READ) str[n++] = 'r' ; else str[n++] = '-' ; if (perm & ACL_WRITE) str[n++] = 'w' ; else str[n++] = '-' ; if (perm & ACL_EXECUTE) str[n++] = 'x' ; else str[n++] = '-' ; str[n++] = '\0' ; return str; } static void dump_acl( const char *type, unsigned char *buf, int siz) { int i = 0; struct posix_acl_xattr_header *header = (typeof(header))buf; struct posix_acl_xattr_entry *entry = (typeof(entry))(header + 1); printf ( "%s version %lx, entries:\n" , type, le32toh(header->a_version)); while ((unsigned char *)entry < buf + siz) { printf ( "\t%s:%s:%s\n" , acl_type(entry), acl_id(entry), acl_perm(entry)); entry++; } } int main( int argc, char **argv) { char buf[512]; char *path = argv[1]; int ret; ret = getxattr(path, "system.posix_acl_default" , buf, sizeof (buf)); if (ret < 0) fprintf (stderr, "get default failed: %m\n" ); else dump_acl( "default" , buf, ret); ret = getxattr(path, "system.posix_acl_access" , buf, sizeof (buf)); if (ret < 0) fprintf (stderr, "get access failed: %m\n" ); else dump_acl( "access" , buf, ret); return 0; } |
acl共享库和命令行:
libacl.so和getfacl、setfacl都在下面的官网释放的源码中,最新版本:acl-2.3.2.tar.xz
上述的信息,没有描述开启ACL后,file mode和ACL具体的对应关系。资料中SuSE那篇文章讲的很详细:
那SuSE的文章中,ACL_USER叫做named user,ACL_GROUP叫作named group。
等价于file mode permission的ACL,叫做minimal ACLs,它有3个条目:owner/owning group/others。
这3个条目之外的,叫做extended ACLs,包括mask、named user、named group
owning group/named group/named user,属于group class。注意这里named user也属于group class,这是我们后面对应的核心。
当只有minimal ACL时,对应是非常简单的,file mode的user/group/other,对应ACL的owner/owning group/other。
开启extended ACL后,user和other还是一一对应的。group的情况有点不一样。文件中的group权限,实际是对应的group class的最大权限。而group class包括owning group/named group/named user,因此,此时file mode group的权限是对应mask权限,因为group class的有效权限都要和mask与,不会超过mask的权限。
With extended ACLs, the group class permissions map to the mask entry permissions,
如下图所示:
看个例子就知道:
$ umask 0
$ mkdir -m 755 test
$ ls -dl test
drwxr-xr-x 1 yuan yuan 0 Dec 25 16:24 test
$ setfacl -m u:nas0:r test
$ getfacl test
# file: test
# owner: yuan
# group: yuan
user::rwx
user:nas0:r--
group::r-x
mask::r-x
other::r-x
$ chmod g-x test
$ getfacl test
# file: test
# owner: yuan
# group: yuan
user::rwx
user:nas0:r--
group::r-x #effective:r--
mask::r--
other::r-x
创建一个目录,权限755,添加一个acl,此时getfacl查看,
mask和group都等于file mode group的权限。
chmod将file mode group的x权限去掉,再次查看acl,此时mask的x权限去掉了,但是group没变。
man 5 acl
man 1 getfacl
man 1 setfacl
acl官网和源码:https://savannah.nongnu.org/projects/acl/
POSIX Access Control Lists on Linux
Andreas Grünbacher SuSE Labs, SuSE Linux AG Nuremberg, Germany agruen@suse.de