当前位置: 首页 > news >正文

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。

http://www.jsqmd.com/news/1003876/

相关文章:

  • 别再找旧脚本了!MMdetection 3.X 版本官方工具一键生成PR曲线图(附完整配置流程)
  • 2026年C语言找工作难吗?普通人还能找到好工作吗?
  • 兰州黄金回收实测 余生珍宝六店行情解析 - 余生黄金回收
  • C盘存储爆红,哪些文件类型可以安全删除?一张清单分三档
  • CVI工程中直接调用自定义DLL的实操资源包(含双项目源码与一键构建脚本)
  • Python生产级API设计:可观测、可演进、可防御的请求生命周期治理
  • TMS320F28335四层小板:6×8cm带USB供电、JTAG下载、复位键和全引脚标注
  • 六盘水珍宝黄金回收测评 2026买金避坑指南 - 余生黄金回收
  • 浙江大学LaTeX论文模板:5分钟快速生成专业毕业论文的终极指南
  • 避开回收套路荆州六大黄金门店测评 - 余生黄金回收
  • 2026年英文降AIGC率指南:别盲目同义词替换!5种降AI高效方法实测(附工具测评) - 降AI实验室
  • C盘大文件怎么搬到D盘或其他分区?从定位到迁移的完整操作
  • 别再只会录宏了!WPS JS宏实战:用filter和箭头函数5分钟搞定数据清洗
  • Spring Boot 文件上传大小限制配置全解析
  • 从英国到葡萄牙,这群欧洲青年为何把目光投向中国开源?
  • 寄行李大件什么物流最省钱?用“寄半折”比价立省一半 - 快递物流资讯
  • 2026甘孜州权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐
  • Logisim 2.7.1 手把手:从零搭建一个支持13种运算的32位MIPS ALU(附完整电路图)
  • 2026年北京企业法律顾问怎么挑?5个核心关键点防踩雷 - 本地品牌推荐
  • STM32CubeMX配置I2C驱动AT24C64 EEPROM,手把手教你搞定用户设置数据存储(附完整工程代码)
  • 2026年q2正规青年旅行社官网品牌技术维度解析:美国旅游/318川藏线自驾游/中国青年旅行社官网/优选推荐 - 优质品牌商家
  • 2026年新中式门楼设计施工服务商评测:五大品牌对比 - 优质品牌商家
  • 保姆级教程:用ADB命令备份与删除长安UNI-V车机自带软件(附完整命令清单)
  • Windows电脑频繁弹广告怎么彻底清除?从定位来源到卸载残留的完整方法
  • 从“滋滋”声到清晰通话:一个移动端音频工程师的AEC避坑实战录
  • 2026年国内篮球架选购全攻略:从材料工艺到工程案例的行业深度调研 - 优质品牌商家
  • 长沙鑫合诚新能源物流车联系电话多少?快速获取 - 工业品牌热点
  • 别再手动填数据了!Vivado 2023.2 中一键生成 .coe 文件并配置 ROM IP 核的保姆级教程
  • 工业吸尘器怎么选?类型、功率、过滤与产区厂商全解析
  • 零样本3D异常检测:GS-CLIP框架的技术突破与应用