Linux inode_permission inode权限检查与acl处理
Linux inode_permission inode权限检查与acl处理
inode_permission()是VFS层最核心的权限检查函数,所有文件系统操作在访问inode之前都会调用该函数验证权限。它封装了基本的DAC检查(mode bit)和ACL检查,同时通过security_inode_permission钩子将检查延伸到LSM模块。
函数定义在fs/namei.c中:
```c
int inode_permission(struct user_namespace *mnt_userns,
struct inode *inode, int mask)
{
int retval;
/* 拒绝无效的访问模式 */
if (unlikely(mask & MAY_WRITE))
BUG_ON(!inode->i_sb);
/* ROOT专用快速路径:root绕过所有DAC检查 */
if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID))
mask &= ~(MAY_READ | MAY_WRITE | MAY_EXEC);
/* 执行DAC检查和ACL处理 */
retval = generic_permission(mnt_userns, inode, mask);
if (retval)
return retval;
/* LSM钩子:security_inode_permission调用各模块的inode_permission */
return security_inode_permission(inode, mask);
}
```
generic_permission()处理UNIX传统权限位和POSIX ACL。其核心逻辑是先执行uid/gid匹配,然后检查对应的owner/group/other权限位,如果基本的mode bit不够再尝试ACL:
```c
int generic_permission(struct user_namespace *mnt_userns,
struct inode *inode, int mask)
{
umode_t mode = inode->i_mode;
kuid_t i_uid = i_uid_into_mnt(mnt_userns, inode);
kgid_t i_gid = i_gid_into_mnt(mnt_userns, inode);
/* exec权限检查:SETUID/SETGID位可能导致权限提升 */
if (mask & MAY_EXEC) {
if (!(mode & S_IXUGO))
return -EACCES;
if (S_ISREG(mode) && (mode & S_ISUID) && mask & MAY_WRITE)
return -ETXTBSY;
}
/* 按owner/group/other检查标准UNIX权限 */
if (uid_eq(current_fsuid(), i_uid)) {
mode >>= 6; /* owner权限位 */
goto done;
}
if (in_group_p(i_gid) || i_uid_eq(i_gid, i_uid)) {
mode >>= 3; /* group权限位 */
goto done;
}
done:
/* mode bit检查通过 */
if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
return 0;
/* mode bit未通过,尝试ACL */
return check_acl(mnt_userns, inode, mask);
}
```
check_acl()调用文件系统提供的get_acl方法获取inode关联的ACL条目,然后逐条遍历进行权限匹配:
```c
static int check_acl(struct user_namespace *mnt_userns,
struct inode *inode, int mask)
{
struct posix_acl *acl;
int error = -EAGAIN;
/* 从inode缓存或文件系统读取ACL */
acl = get_inode_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl) {
/* 执行POSIX ACL权限检查 */
error = posix_acl_permission(mnt_userns, inode, acl, mask);
posix_acl_release(acl);
}
return error;
}
```
posix_acl_permission()遍历ACL条目进行精确匹配:
```c
int posix_acl_permission(struct user_namespace *mnt_userns,
struct inode *inode, const struct posix_acl *acl,
int want)
{
const struct posix_acl_entry *pa, *pe, *mask_obj;
int found = 0;
/* ACL_USER_OBJ条目 */
pa = acl->a_entries;
pe = pa + acl->a_count;
for_each_possible_acl(pa, pe, ACL_USER_OBJ) {
if (uid_eq(current_fsuid(), i_uid_into_mnt(mnt_userns, inode))) {
found = ACL_USER_OBJ;
break;
}
}
/* ACL_USER条目: 匹配特定UID */
for_each_possible_acl(pa, pe, ACL_USER) {
kuid_t acl_uid = make_kuid(&init_user_ns, pa->e_id.uid);
if (uid_eq(acl_uid, current_fsuid())) {
found = ACL_USER;
goto check;
}
}
/* ACL_GROUP_OBJ和ACL_GROUP: 匹配GID */
for_each_possible_acl(pa, pe, ACL_GROUP_OBJ) {
if (in_group_p(i_gid_into_mnt(mnt_userns, inode))) {
found = ACL_GROUP_OBJ;
break;
}
}
for_each_possible_acl(pa, pe, ACL_GROUP) {
kgid_t acl_gid = make_kgid(&init_user_ns, pa->e_id.gid);
if (in_group_p(acl_gid)) {
found = ACL_GROUP;
goto check;
}
}
/* ACL_MASK存在则使用mask限制权限 */
check:
mask_obj = NULL;
for_each_possible_acl(pa, pe, ACL_MASK) {
mask_obj = pa;
break;
}
if (found) {
unsigned int granted = 0;
switch (found) {
case ACL_USER_OBJ:
granted = ACL_ENTRY_MASK(pa);
break;
case ACL_USER:
granted = ACL_ENTRY_MASK(pa);
if (mask_obj)
granted &= ACL_ENTRY_MASK(mask_obj);
break;
case ACL_GROUP_OBJ:
case ACL_GROUP:
granted = ACL_ENTRY_MASK(pa);
if (mask_obj)
granted &= ACL_ENTRY_MASK(mask_obj);
break;
}
want &= MAY_READ | MAY_WRITE | MAY_EXEC;
if ((want & ~granted) == 0)
return 0;
}
return -EACCES;
}
```
permission()是更高层的封装,被openat、mkdir、truncate等系统调用广泛使用。它在inode_permission的基础上增加了文件系统特定的permission回调:
```c
int permission(struct user_namespace *mnt_userns,
struct inode *inode, int mask)
{
bool submask;
int ret;
/* 如果inode的FS实现了permission操作,优先调用 */
if (inode->i_opflags & IOP_FASTPERM)
return inode_permission(mnt_userns, inode, mask);
if (inode->i_op->permission) {
ret = inode->i_op->permission(mnt_userns, inode, mask);
if (ret != -EOPNOTSUPP)
return ret;
}
return inode_permission(mnt_userns, inode, mask);
}
```
一些文件系统如ext4和btrfs实现了自定义的permission回调,在generic_permission基础上增加文件系统特有的检查。例如ext4的ext4_permission在generic_permission之前检查文件系统的只读状态和加密属性。整个inode权限检查的完整链路为:permission -> inode_permission -> generic_permission -> check_acl -> posix_acl_permission -> security_inode_permission。
