Linux 安全 - DAC机制

文章目录

  • 一、安全简介
  • 二、DAC
    • 2.1 UNIX 的自主访问控制
    • 2.2 Linux 的自主访问控制
  • 三、进程凭证
    • 3.1 简介
    • 3.2 uid/gid
    • 3.3 系统调用
  • 四、客体标记
    • 4.1 简介
    • 4.2 系统调用
  • 五、UGO规则源码分析
  • 参考资料

一、安全简介

计算机系统应对安全挑战的办法大致有四种:隔离、控制、混淆、监视。
(1)隔离:计算机系统安全的设计者在系统的各个层级都发明了不同的技术来实现隔离,隔离的结果常常被称作“沙箱”。隔离是对外的,阻断内部和外部的交互。
(2)控制:则是对内的,在计算机世界是通过系统代码内在的逻辑和安全策略来维护信息流动和信息改变。如用户 A 可不可以读取文件 a,用户 B 能不能改变文件 b 的内容,等等。
(3)混淆:加密。可以接触到数据却无法还原信息。加密的本质是通过计算来混淆数据,让攻击者在不知道密钥的情况下很难将加密后的数据还原为原始数据。
(4)监视:计算机系统中的日志和审计就是在做监视工作。

为了更有效地利用计算机,计算机操作系统步入分时多用户时代。比如UNIX ,UNIX 是诞生于 20 世纪 70 年代的分时多任务多用户操作系统。当时的场景是许多用户同时登录到一台主机,运行多个各自的进程。许多人登录到一台主机,张三是个程序员,李四是个文档管理员,王五是系统管理员。随之出现了基于角色的访问控制(Role-based Acess Control,RBAC),让用户分属于不同的角色,再基于角色赋予访问权限。

当 PC 时代来临,计算机设备专属于某个人,系统中的所谓用户也背离了原有的含义。随便打开 Linux 系统上的/etc/passwd 文件,看看里面还有几个是真正的用户?因此,在PC 中使用基于角色的访问控制就有些力不从心了。接下来诞生了另一个访问控制模型——类型增强(Type Enforcement,TE)。模型中控制的对象不再是人,或角色,而是进程。进程属于不同的类型,不同类型有不同的访问权限。

二、DAC

访问的三要素是主体、操作和客体。主体是代表用户执行任务的进程。客体有很多种,包
括文件、目录、管道、设备、IPC(进程间通信)、socket(套接字)、key(密钥)等。操作其实就是系统调用。

对照访问的三个要素,有了主体标记(请参考第三节)和客体标记(请参考第四节),只要有一种方法规定哪个主体可以对哪个客体进行何种操作就可以做到访问控制了。

访问控制(Access Control):是对访问进行控制。比如,允许进程 A 读文件 a,不允许进程 B 读文件 b。要实现访问控制,需要两个东西,一个是标记,标记主体和客体,这样才有控制的对象;另一个是策略,允许某主体对某客体做什么。

自主访问控制(Discretionary Access Control):
(1)UGO
(2)ACL
(3)Capabilities

2.1 UNIX 的自主访问控制

UNIX 的自主访问控制的设计是简单而有效的。它分为两个部分,第一部分可以概括为进程操作文件。操作分三种:读、写、执行。在进程操作文件时,内核会检查进程有没有对文件的相应操作许可。第二部分可以概括为:拥有特权的进程可以做任何事情,内核不限制。特权机制实际上包含了两类行为,一类是超越第一部分的操作许可控制,比如 root 用户可以读或写任何文件。另一类是无法纳入上述“进程操作文件”模型之内的行为,比如重启动系统。

Unix系统的UGO(User、Group、Other)权限管理方式在文件和目录上设置权限位,用来控制用户或用户组对文件或目录的访问。Linux继承了Unix的UGO权限管理方式。

对于这种DAC模型:
主体:一般指进程。
客体:一般指文件。
行为(操作):读取权限(r)、写入权限(w)和执行权限(x)。
主体 操作 客体时查询的规则(UGO规则):把主体分为User、Group、Other三种类型,每种类型拥有自己的RWX mask。

每个文件和目录都有一个所有者和所属组,以及一组权限位。权限位包括读取权限(r)、写入权限(w)和执行权限(x)。权限位可以分为三个类别:所有者权限(User)、所属组权限(Group)和其他用户权限(Others)。这些权限位决定了哪些用户或用户组可以对文件执行特定的操作。

例如,如果一个文件的权限位设置为-rw-r–r–,表示文件的所有者具有读写权限,所属组用户具有读权限,其他用户也具有读权限,但没有写权限。

DAC是一种简单而直观的访问控制机制,但它具有一定的局限性。例如,DAC不能提供细粒度的访问控制,无法限制特定用户对特定文件的访问。此外,它无法提供强制性的安全策略,因此需要其他访问控制机制来提供更高级别的安全性。

为了解决这些限制,Linux还引入了其他访问控制机制,如访问控制列表(ACLs)、Capabilities、Linux 安全模块LSM等。

2.2 Linux 的自主访问控制

Linux继承了Unix的UGO权限管理方式。

在自主访问控制上,Linux 对 UNIX 的扩展主要有两处,一是提供了访问控制列表(Access
Control List),使得能够规定某一个用户或某一个组的操作许可;二是对特权操作细化(Capabilities),将原有属于根用户的特权细化为互不相关的三十几个能力。

三、进程凭证

没有标记就谈不上区分,没有区分就无从实施控制,接下来介绍进程的标记。

UNIX 是诞生于 20 世纪 70 年代的分时多任务多用户操作系统。当时的场景是许多用户同时登录到一台主机,运行多个各自的进程。因此,很自然的,UNIX 系统中进程的标记是基于用户的。在人类的世界中,人的标记是名字。相比字符串而言,计算机更擅长处理数字,UNIX使用一个整数来标记运行进程的用户,这个整数被称作 user id,简写为 uid。人通常被分组,比如这几个人做研发工作,被分到研发组,那几个人做销售工作,被分到销售组。UNIX 用另一个整数来标记用户组,这个整数被称作 group id,简写为 gid。uid 和 gid 是包括 Linux 在内的所有类 UNIX 操作系统的自主访问控制的基础。

3.1 简介

下面看一下 uid 和 gid 是如何记录在内核的进程控制结构之中的:

struct task_struct {/* Process credentials: *//* Tracer's credentials at attach: */const struct cred __rcu		*ptracer_cred;/* Objective and real subjective task credentials (COW): */const struct cred __rcu		*real_cred;/* Effective (overridable) subjective task credentials (COW): */const struct cred __rcu		*cred;
}

Linux 内核没有用户这个数据结构。内核中只会识别UID,至于用户名是通过在/etc/passwd文件中查找对应关系得到的。

这里我们只关系 real_cred 和 cred 这两个进程凭证。进程的凭证中存储有和访问控制相关的成员。

在内核代码注释中将 real_cred 称为客体(objective)凭证,将 cred 称为主体(subjective)
凭证。进程是主体,在某些场景下又是客体。典型的场景是进程间发信号,进程 A 向进程 B 发
送信号,进程 A 是主体,进程 B 就是客体。在大多数情况下,主体凭证和客体凭证的值是相同
的,但在某些情况下内核代码会修改当前进程的主体凭证,以获得某种访问权限,待执行完任
务后再将主体凭证改回原值,比如SUID机制,请参考:Linux 安全 - SUID机制。

权限管理时真正代表用户的是进程,操作文件的也是进程,也就是说用户所拥有的文件访问权限是通过进程来体现的。

凭证的数据结构:

/** The security context of a task** The parts of the context break down into two categories:**  (1) The objective context of a task.  These parts are used when some other*	task is attempting to affect this one.**  (2) The subjective context.  These details are used when the task is acting*	upon another object, be that a file, a task, a key or whatever.** Note that some members of this structure belong to both categories - the* LSM security pointer for instance.** A task has two security pointers.  task->real_cred points to the objective* context that defines that task's actual details.  The objective part of this* context is used whenever that task is acted upon.** task->cred points to the subjective context that defines the details of how* that task is going to act upon another object.  This may be overridden* temporarily to point to another security context, but normally points to the* same context as task->real_cred.*/
struct cred {......kuid_t		uid;		/* real UID of the task */kgid_t		gid;		/* real GID of the task */kuid_t		suid;		/* saved UID of the task */kgid_t		sgid;		/* saved GID of the task */kuid_t		euid;		/* effective UID of the task */kgid_t		egid;		/* effective GID of the task */kuid_t		fsuid;		/* UID for VFS ops */kgid_t		fsgid;		/* GID for VFS ops */unsigned	securebits;	/* SUID-less security management */kernel_cap_t	cap_inheritable; /* caps our children can inherit */kernel_cap_t	cap_permitted;	/* caps we're permitted */kernel_cap_t	cap_effective;	/* caps we can actually use */kernel_cap_t	cap_bset;	/* capability bounding set */kernel_cap_t	cap_ambient;	/* Ambient capability set */
#ifdef CONFIG_KEYSunsigned char	jit_keyring;	/* default keyring to attach requested* keys to */struct key	*session_keyring; /* keyring inherited over fork */struct key	*process_keyring; /* keyring private to this process */struct key	*thread_keyring; /* keyring private to this thread */struct key	*request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITYvoid		*security;	/* subjective LSM security */
#endif......
} __randomize_layout;

进程凭证中不止有 id 相关的成员,还有能力集相关的成员、密钥串相关的成员和强制访问
控制相关的成员。

3.2 uid/gid

struct cred {......kuid_t		uid;		/* real UID of the task */kgid_t		gid;		/* real GID of the task */kuid_t		suid;		/* saved UID of the task */kgid_t		sgid;		/* saved GID of the task */kuid_t		euid;		/* effective UID of the task */kgid_t		egid;		/* effective GID of the task */kuid_t		fsuid;		/* UID for VFS ops */kgid_t		fsgid;		/* GID for VFS ops */......
}

可以看到进程凭证结构体struct cred包含了多个 uid/gid。

(1)uid
这是最早出现的 user id。有时也被称为 real uid,实际的 uid,简写为®uid,代表启动进程的原始用户身份。这个 uid 在资源统计和资源分配中使用,比如限制某用户拥有的进程数量。
(2)euid
euid(effective uid)即有效 uid。操作系统用于确定进程权限和访问权限的用户 ID。它表示进程在访问系统资源时所扮演的用户身份。
在内核做特权判断时使用它。它的引入和提升权限有关。此外,内核在做 ipc(进程间通信)和 key(密钥)的访问控制时也使用 euid。
权限判断时看的就是euid。初始状态时,uid和euid相同,做一些权限切换时euid可能改变不等于uid了。
(3)suid
suid 是“saved set user id”。suid 存储了有效用户 ID,以便稍后恢复。euid 和特权有关,当 euid 为 0 时,进程就具有了超级用户的权限,拥有了全部特权,在系统中没有做不了的事情。这有些危险。我们需要锋利的刀,但不用的时候希望把刀放入刀鞘。为了让进程不要总是具有全部特权,总能为所欲为,系统的设计者引入了suid,用于暂存 euid 的值。euid 为 0 时做需要特权的操作,执行完操作,将 0 赋予suid,euid 恢复为非 0 值,做普通的不需要特权的操作,需要特权时再将 suid 的值传给 euid。
(4)fsuid
fsuid 是“file system user id”。这个 uid 是 Linux 系统独有的。它用于在文件系统相关的访问控制中判断操作许可。

3.3 系统调用

进程的控制结构和进程凭证都是内核中的数据结构,相应的数据对象都是被内核掌控的。用户态进程只能通过内核提供的接口来查看和修改进程凭证。Linux 内核提供了数个系统调用来查看和修改进程的凭证中的uid和gid。这部分系统调用都要求进程只能修改自己的uid和gid,不可以修改别的进程的。
(1)

NAMEsetuid - set user identitySYNOPSIS#include <sys/types.h>#include <unistd.h>int setuid(uid_t uid);
DESCRIPTIONsetuid() sets the effective user ID of the calling process.  If the effective UID of the caller is root, the real UID and saved set-user-ID are also set.

setuid() 函数用于设置调用进程的有效用户 ID(euid)。如果调用者的有效用户 ID 是 root,那么实际用户 ID(uid) 和保存的设置用户 ID(suid) 也会被设置。

(2)

NAMEseteuid, setegid - set effective user or group IDSYNOPSIS#include <sys/types.h>#include <unistd.h>int seteuid(uid_t euid);int setegid(gid_t egid);
DESCRIPTIONseteuid()  sets  the effective user ID of the calling process.  Unprivileged user processes may only set the effective user ID to the real user ID, the effective user IDor the saved set-user-ID.Precisely the same holds for setegid() with "group" instead of "user".

seteuid() 函数用于设置调用进程的有效用户 ID。非特权用户进程只能将有效用户 ID 设置为实际用户 ID、有效用户 ID 或保存的设置用户 ID。

在 Linux 系统中,非特权用户进程的有效用户 ID 受到限制,只能设置为它自身的实际用户 ID、有效用户 ID 或保存的设置用户 ID。这是为了确保进程无法将有效用户 ID 设置为其他特权用户的用户 ID,从而防止滥用权限。

这种限制是为了维护系统的安全性,防止非特权用户进程滥用特权。只有特权用户(例如 root 用户)才能更改为任意有效用户 ID。

(3)

NAMEsetreuid, setregid - set real and/or effective user or group IDSYNOPSIS#include <sys/types.h>#include <unistd.h>int setreuid(uid_t ruid, uid_t euid);int setregid(gid_t rgid, gid_t egid);
DESCRIPTIONsetreuid() sets real and effective user IDs of the calling process

setreuid() 函数用于设置调用进程的实际用户 ID(real user ID)和有效用户 ID(effective user ID)。

当给定的实际用户 ID 或有效用户 ID 的值为 -1 时,系统将保持该 ID 不变。

非特权进程只能将有效用户 ID 设置为实际用户 ID、有效用户 ID 或保存的设置用户 ID。

非特权用户只能将实际用户 ID 设置为实际用户 ID 或有效用户 ID。

如果设置了实际用户 ID,或者将有效用户 ID 设置为与先前的实际用户 ID 不相等的值,保存的设置用户 ID 将被设置为新的有效用户 ID。

通过调用setreuid()函数,进程可以更改其实际用户 ID 和有效用户 ID。这对于需要在进程中切换用户身份的场景非常有用。

(4)

NAMEsetresuid, setresgid - set real, effective and saved user or group IDSYNOPSIS#define _GNU_SOURCE         /* See feature_test_macros(7) */#include <unistd.h>int setresuid(uid_t ruid, uid_t euid, uid_t suid);int setresgid(gid_t rgid, gid_t egid, gid_t sgid);
DESCRIPTIONsetresuid() sets the real user ID, the effective user ID, and the saved set-user-ID of the calling process.

setresuid() 函数用于设置调用进程的实际用户 ID(real user ID)、有效用户 ID(effective user ID)和保存的设置用户 ID(saved set-user-ID)。

非特权用户进程可以将实际用户 ID、有效用户 ID 和保存的设置用户 ID 分别设置为以下值之一:当前实际用户 ID、当前有效用户 ID 或当前保存的设置用户 ID。

特权进程(在 Linux 上,拥有 CAP_SETUID 能力的进程)可以将实际用户 ID、有效用户 ID 和保存的设置用户 ID 设置为任意值。

如果其中一个参数等于 -1,则对应的值不会改变。

无论对实际用户 ID、有效用户 ID 和保存的设置用户 ID 进行了何种更改,文件系统的用户 ID 总是设置为与(可能是新的)有效用户 ID 相同的值。

通过调用 setresuid() 函数,进程可以同时更改实际用户 ID、有效用户 ID 和保存的设置用户 ID。这对于需要在进程中切换用户身份的场景非常有用。

(5)

NAMEsetfsuid - set user identity used for file system checksSYNOPSIS#include <unistd.h> /* glibc uses <sys/fsuid.h> */int setfsuid(uid_t fsuid);
DESCRIPTIONThe  system call setfsuid() sets the user ID that the Linux kernel uses to check for all accesses to the file system.  Normally, the value of fsuid will shadow the valueof the effective user ID.  In fact, whenever the effective user ID is changed, fsuid will also be changed to the new value of the effective user ID.

setfsuid() 系统调用用于设置 Linux 内核用于检查对文件系统的所有访问的用户 ID。通常情况下,fsuid 的值会跟随有效用户 ID 的值。事实上,每当有效用户 ID 发生变化时,fsuid 也会被更改为新的有效用户 ID 的值。

通过调用 setfsuid() 函数,可以显式地设置文件系统用户 ID(fsuid)。这个值将影响进程对文件系统的访问权限检查。如果 fsuid 不同于有效用户 ID,那么内核将使用 fsuid 来进行访问权限检查。

设置 fsuid 的主要目的是允许进程在文件系统访问权限方面切换用户身份。这对于需要使用不同用户身份访问文件系统的场景非常有用,例如在特定用户权限下处理文件或资源。通过显式设置 fsuid,进程可以模拟以其他用户身份执行操作,而不必更改有效用户 ID。

(6)
在设置 user id 的系统调用中,内核代码遵守了以下原则:
(1)具备 setuid 特权的进程可以把®uid、euid、suid、fsuid 设置为任意值。即如果进程有root权限,那么uid/suid/euid/fsuid可以设置任意值。
(2)不具备 setuid 特权的进程只能将®uid、euid、suid 的值设置为现有的®uid、euid、或 suid
的值。以 euid 为例,euid 的新值只能是现在的®uid 的值、现在的 euid 的值或现在的 suid
的值。即如果进程没有root权限,那么设置新的uid只能是原uid/suid/euid/fsuid中的一个值,不能是任意值。
(3)不具备 setuid 特权的进程只能将 fsuid 的值置为现有的®uid、euid、suid、fsuid 的值
之一。

setreuid()/setuid()/setresuid()系统调用可以降级权限,它没有升级的能力。

在用户权限切换的过程中,一般通过SUID机制来无密码的把权限升级到root,然后在root状态下验证用户密码,再根据配置通过setreuid()/setuid()/setresuid()系统调用到权限降级到合适用户。

四、客体标记

4.1 简介

UNIX 中进程有多个 uid 和多个 gid,但是文件只有一个 uid 和一个 gid,分别称为属主和属组。它们的含义是标记文件属于哪一个用户,属于哪一个用户组。

在文件系统中一个文件不仅要包含文件的内容(数据),还要包含所谓的元数据(meta data)。
元数据包括文件的存取访问方式、文件的创建日期、文件所在的设备、属主和属组以及允许位等。

在 UNIX 中,元数据存储在文件系统的 inode 中。有些文件系统,比如 ext4,在物理存储介质上存储 inode。有些非 UNIX/Linux 原生的文件系统没有 inode概念。Linux 内核会在内存中临时创建 inode。对于那些内存文件系统,比如 tmpfs,Linux 内核也是在内存中创建临时的 inode。

下面看一下内核中 inode 的定义,这里我们只看成员文件所在的属主和属组:

struct inode {......kuid_t			i_uid;kgid_t			i_gid;......
}

4.2 系统调用

文件属性是内核控制的数据,只能由内核来修改。内核开放了几个系统调用供用户态进程使用,用户态进程可以请求内核修改文件属性。涉及属主和属组的系统调用是:

NAMEchown, fchown, lchown - change ownership of a fileSYNOPSIS#include <unistd.h>int chown(const char *path, uid_t owner, gid_t group);int fchown(int fd, uid_t owner, gid_t group);int lchown(const char *path, uid_t owner, gid_t group);

这些系统调用用于更改文件的所有者和组。它们的区别仅在于文件的指定方式:
chown() 函数更改路径指定的文件的所有者,如果该文件是符号链接,则对其进行解引用。
fchown() 函数更改由打开的文件描述符 fd 引用的文件的所有者。
lchown() 函数类似于 chown(),但不对符号链接进行解引用。

只有特权进程(Linux 上具有 CAP_CHOWN 能力的进程)才能更改文件的所有者。文件的所有者可以将文件的组更改为该所有者所属的任何组。特权进程(Linux 上具有 CAP_CHOWN 能力的进程)可以任意更改组。

如果将所有者或组指定为 -1,则该 ID 不会更改。

当非特权用户更改可执行文件的所有者或组时,S_ISUID 和 S_ISGID 模式位将被清除。POSIX 并未指定在 root 用户执行 chown() 时是否也应该清除这些模式位,Linux 的行为取决于内核版本。对于非组可执行文件(即 S_IXGRP 位未设置的文件),S_ISGID 位指示强制锁定,并且不会被 chown() 清除。

五、UGO规则源码分析

UGO规则请参考:2.1 UNIX 的自主访问控制

客体文件inode->i_mode中存储了UGO 3组mask,每组mask由rwx三个行为组成。

struct inode {umode_t			i_mode;......
}

有了UGO规则,主体进程在RWX客体文件时会做对应的权限规则检查。

以open系统调用为例:

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{if (force_o_largefile())flags |= O_LARGEFILE;return do_sys_open(AT_FDCWD, filename, flags, mode);
}SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,umode_t, mode)
{if (force_o_largefile())flags |= O_LARGEFILE;return do_sys_open(dfd, filename, flags, mode);
}

open系统调用会调用inode_permission函数进行权限检查:

/*** inode_permission - Check for access rights to a given inode* @inode: Inode to check permission on* @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)** Check for read/write/execute permissions on an inode.  We use fs[ug]id for* this, letting us set arbitrary permissions for filesystem access without* changing the "normal" UIDs which are used for other things.** When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.*/
int inode_permission(struct inode *inode, int mask)
{int retval;retval = sb_permission(inode->i_sb, inode, mask);if (retval)return retval;if (unlikely(mask & MAY_WRITE)) {/** Nobody gets write access to an immutable file.*/if (IS_IMMUTABLE(inode))return -EPERM;/** Updating mtime will likely cause i_uid and i_gid to be* written back improperly if their true value is unknown* to the vfs.*/if (HAS_UNMAPPED_ID(inode))return -EACCES;}retval = do_inode_permission(inode, mask);if (retval)return retval;retval = devcgroup_inode_permission(inode, mask);if (retval)return retval;return security_inode_permission(inode, mask);
}
EXPORT_SYMBOL(inode_permission);

从函数注释可以看到:

Check for read/write/execute permissions on an inode.

UGO权限检查:

inode_permission()-->do_inode_permission()-->generic_permission()
/*** generic_permission -  check for access rights on a Posix-like filesystem* @inode:	inode to check access rights for* @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, ...)** Used to check for read/write/execute permissions on a file.* We use "fsuid" for this, letting us set arbitrary permissions* for filesystem access without changing the "normal" uids which* are used for other things.** generic_permission is rcu-walk aware. It returns -ECHILD in case an rcu-walk* request cannot be satisfied (eg. requires blocking or too much complexity).* It would then be called again in ref-walk mode.*/
int generic_permission(struct inode *inode, int mask)
{int ret;/** Do the basic permission checks.*/ret = acl_permission_check(inode, mask);if (ret != -EACCES)return ret;if (S_ISDIR(inode->i_mode)) {/* DACs are overridable for directories */if (!(mask & MAY_WRITE))if (capable_wrt_inode_uidgid(inode,CAP_DAC_READ_SEARCH))return 0;if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))return 0;return -EACCES;}/** Searching includes executable on directories, else just read.*/mask &= MAY_READ | MAY_WRITE | MAY_EXEC;if (mask == MAY_READ)if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH))return 0;/** Read/write DACs are always overridable.* Executable DACs are overridable when there is* at least one exec bit set.*/if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))return 0;return -EACCES;
}
EXPORT_SYMBOL(generic_permission);

在acl_permission_check函数中做UGO权限检查:

/** This does the basic permission checking*/
static int acl_permission_check(struct inode *inode, int mask)
{//获取文件的inode->i_modeunsigned int mode = inode->i_mode;//查当前文件系统用户 ID(current_fsuid())是否等于 inode 的用户 ID(i_uid)。如果它们相等,表示用户与文件的所有者匹配,它将 mode 按位向右移动 6 位(获取所有者的权限)if (likely(uid_eq(current_fsuid(), inode->i_uid)))mode >>= 6;else {//如果当前用户不是所有者,函数会检查 inode 是否启用了 POSIX ACL,并且组权限是否已设置。if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {//如果两个条件都满足,它会使用 check_acl() 函数执行额外的基于 ACL 的权限检查。int error = check_acl(inode, mask);if (error != -EAGAIN)return error;}//如果当前用户不是所有者,并且未执行 ACL 检查或 ACL 检查结果为 -EAGAIN,函数会检查当前用户是否属于文件所属组。如果是这样,它会考虑组对请求访问的权限。它检查当前用户是否是 inode 所属组的成员(in_group_p(inode->i_gid))。如果是这样,它将 mode 按位向右移动 3 位(获取组的权限)。if (in_group_p(inode->i_gid))mode >>= 3;}//如果以上条件都未匹配成功,则为Other用户,取最低3bit规则(获取其他用户权限)/** If the DACs are ok we don't need any capability check.*///将请求的访问掩码与相关权限(mode)进行比较,基于用户与文件的关系(所有者、组成员或其他用户)。如果允许请求的访问,它返回 0 表示成功。否则,返回 -EACCES 表示权限被拒绝。 if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)return 0;return -EACCES;
}

(1)该函数通常在需要进行权限检查的文件系统操作中调用,比如打开文件、读写文件或执行文件等操作。
inode 参数表示文件或目录的元数据,包括权限、所有权、时间戳和文件大小等属性。
mask 参数是一个访问掩码,用于指定请求的权限,比如读取、写入或执行权限。
函数根据 inode 的 mode 位执行权限检查。这些位表示所有者、组和其他用户的权限。

(2)函数首先检查当前用户是否与文件的所有者匹配。如果匹配,它会考虑所有者对请求访问的权限。它检查当前文件系统用户 ID(current_fsuid())是否等于 inode 的用户 ID(i_uid)。如果它们相等,表示用户与文件的所有者匹配,它将 mode 按位向右移动 6 位(获取所有者的权限)

(3)如果当前用户不是所有者,函数会检查 inode 是否启用了 POSIX ACL,并且组权限是否已设置。如果两个条件都满足,它会使用 check_acl() 函数执行额外的基于 ACL 的权限检查。
如果 ACL 检查返回除了 -EAGAIN 之外的错误码,表示明确拒绝权限,函数会立即返回该错误,不再进行进一步处理。

(4)如果当前用户不是所有者,并且未执行 ACL 检查或 ACL 检查结果为 -EAGAIN,函数会检查当前用户是否属于文件所属组。如果是这样,它会考虑组对请求访问的权限。它检查当前用户是否是 inode 所属组的成员(in_group_p(inode->i_gid))。如果是这样,它将 mode 按位向右移动 3 位(获取组的权限)。

(5)如果以上条件都未匹配成功,则为Other用户,取最低3bit规则(获取其他用户权限)。

(6)最后,函数将请求的访问掩码与相关权限(mode)进行比较,基于用户与文件的关系(所有者、组成员或其他用户)。如果允许请求的访问,它返回 0 表示成功。否则,返回 -EACCES 表示权限被拒绝。

参考资料

Linux 5.4.18

Linux DAC 权限管理详解
Linux 内核安全模块深入剖析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/103753.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Spring Boot】RabbitMQ消息队列 — RabbitMQ入门

💠一名热衷于分享知识的程序员 💠乐于在CSDN上与广大开发者交流学习。 💠希望通过每一次学习,让更多读者了解我 💠也希望能结识更多志同道合的朋友。 💠将继续努力,不断提升自己的专业技能,创造更多价值。🌿欢迎来到@"衍生星球"的CSDN博文🌿 🍁本…

GEO生信数据挖掘(七)差异基因分析

上节&#xff0c;我们使用结核病基因数据&#xff0c;做了一个数据预处理的实操案例。例子中结核类型&#xff0c;包括结核&#xff0c;潜隐进展&#xff0c;对照和潜隐&#xff0c;四个类别。本节延续上个数据&#xff0c;进行了差异分析。 差异分析 计算差异指标step12 加载…

蓝桥杯 题库 简单 每日十题 day13

01 乘积尾零 题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将 所填结果输出即可。如下的10行数据&#xff0c;每行有10个整数&#xff0c;请你求出它们的乘积的末尾有多少个零&#xff1f; 5650 4542 3554 473 946 4114 3871 9073 90 …

汽车RNC主动降噪算法DSP C程序实现

汽车RNC主动降噪算法C程序 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,车载

rust 智能指针

智能指针 Box Box 的使用场景 由于 Box 是简单的封装&#xff0c;除了将值存储在堆上外&#xff0c;并没有其它性能上的损耗。而性能和功能往往是鱼和熊掌&#xff0c;因此 Box 相比其它智能指针&#xff0c;功能较为单一&#xff0c;可以在以下场景中使用它&#xff1a; 特…

Flink之DataStream API开发Flink程序过程与Flink常见数据类型

开发Flink程序过程与Flink常见数据类型 DataStream APIFlink三层APIDataStream API概述 开发Flink程序过程添加依赖创建执行环境执行模式创建Data Source应用转换算子创建Data Sink触发程序执行示例 Flink常见数据类型基本数据类型字符串类型时间和日期类型数组类型元组类型列表…

【通信系列 6 -- AT 命令介绍】

文章目录 1. 背景介绍1.2 AT的命令格式1.3 AT指令用法1.3.1 指令执行结果 1.2 CP 常用AT指令1.2.1 CP 模式设置1.2.2 网络相关1.2.3 IP获取1.2.4 Band 设置1.2.5 电话相关1.2.6 SIM卡检测1.2.7 cmwap 和cmnet1.2.8 AT 写 IMEI 1. 背景介绍 AT 命令一般分为三种&#xff1a; C…

1.1 向量与线性组合

一、向量的基础知识 两个独立的数字 v 1 v_1 v1​ 和 v 2 v_2 v2​&#xff0c;将它们配对可以产生一个二维向量 v \boldsymbol{v} v&#xff1a; 列向量 v v [ v 1 v 2 ] v 1 v 的第一个分量 v 2 v 的第二个分量 \textbf{列向量}\,\boldsymbol v\kern 10pt\boldsymbol …

【Java】如何将File转换成MultipartFile

假如你把你的后端项目部署在服务器上的时候&#xff0c;然后你要去读取某个路径下的文件&#xff0c;然后你就要提供文件的路径&#xff0c;然后获取到该文件对象&#xff0c;但是你需要将当前文件对象File转换成MultipartFile再发送http请求传递到其他服务器上&#xff0c;这样…

GPIO子系统(三)

1&#xff0c;简述 GPIO 资源是相对来说较为简单&#xff0c;而且比较通用&#xff08;比如 LED 灯&#xff09;&#xff0c;而 Linux 的 GPIO 驱动属于 Linux Driver 中较为容易上手的部分&#xff0c;但是简单归简单&#xff0c;在 Linux 系统中&#xff0c;要使用 GPIO 资源…

mysql.mongoDb,neo4j数据库对比

Mysql与MongoDb和Neo4j的一些对比 主要区别 MySQL&#xff1a; 1.MySQL是一种关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;广泛用于处理结构化数据。 2.它支持SQL语言&#xff0c;具备成熟的事务处理和数据一致性能力。 3.MySQL适用于大多数传统的基于表格…

UE4和C++ 开发-头文件(.h) 和实现文件(.cpp)区别

.h文件和.cpp文件是C程序中的两种不同类型的文件。 .h文件通常包含类、函数和变量的声明&#xff0c; 而.cpp文件包含这些声明的实现。 .h文件中的声明通常是公共的&#xff0c;可以被其他文件包含和使用。.cpp文件中的实现通常是私有的&#xff0c;只能在该文件中使用。 在…

高级网络调试技巧:使用Charles Proxy捕获和修改HTTP/HTTPS请求

今天我将与大家分享一种强大的网络调试技巧&#xff0c;那就是使用Charles Proxy来捕获和修改HTTP/HTTPS请求。如果您是一位开发人员或者网络调试爱好者&#xff0c;那么这个工具肯定对您有着很大的帮助。接下来&#xff0c;让我们一起来学习如何使用Charles Proxy进行高级网络…

区块链加密虚拟货币交易平台安全解决方案

区块链机密货币交易锁遭入侵&#xff0c;安全存在隐患。使用泰雷兹Protect server HSM加密机&#xff0c;多方位保护您的数据&#xff0c;并通过集中化管理&#xff0c;安全的存储密钥。 引文部分&#xff1a; 损失7000万美元!黑客入侵香港区块链加密货币交易所 2023年9月&…

计算机毕业设计选什么题目好?springboot 健身房管理系统

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

HTML5简介-HTML5 新增语义化标签-HTML5 新增多媒体标签

一、HTML5简介 HTML5&#xff0c;全称为HyperText Markup Language 5&#xff0c;是HTML的第五个版本&#xff0c;由万维网联盟&#xff08;World Wide Web Consortium&#xff0c;W3C&#xff09;和Web Hypertext Application Technology Working Group&#xff08;WHATWG&am…

github创建个人网页登录后404无法显示的问题

1.首先必须要有内容&#xff0c;默认是会找index.html文件&#xff0c;找不到该文件会找readme.md文件&#xff0c;也就是说最简单的方法是&#xff0c;创建了与用户名同名的repository后username.github.io后&#xff0c;添加一个readme.md文件&#xff0c;得在readme里打点字…

十四、【图章工具组】

文章目录 仿制图章图案图章 仿制图章 纺织图和章工具跟我们之前所用到的修补工具类似,需要我们先按住Alt键选住一块区域&#xff0c;然后调整它的硬度在用我们选择的区域去覆盖&#xff0c;需要注意的是&#xff0c;我们去做的时候尽量一笔覆盖我们想要遮住的区域: 图案图章…

Blender:对模型着色

Blender&#xff1a;使用立方体制作动漫头像-CSDN博客 上一步已经做了一个头像模型&#xff0c;我做的太丑了&#xff0c;就以这个外星人头像为例 首先切换到着色器编辑器 依次搜索&#xff1a;纹理坐标、映射、分离xyz和颜色渐变 这里的功能也是非常丰富和强大&#xff0c…

【微服务部署】九、使用Docker Compose搭建高可用双机热备MySQL数据库

通常&#xff0c;一般业务我们使用云服务器提供的数据库&#xff0c;无论是MySQL数据库还是其他数据库&#xff0c;云服务厂商都提供了主备功能&#xff0c;我们不需要自己配置处理。而如果需要我们自己搭建数据库&#xff0c;那么考虑到数据的高可用性、故障恢复和扩展性&…