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

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 内核安全模块深入剖析

相关文章:

Linux 安全 - DAC机制

文章目录 一、安全简介二、DAC2.1 UNIX 的自主访问控制2.2 Linux 的自主访问控制 三、进程凭证3.1 简介3.2 uid/gid3.3 系统调用 四、客体标记4.1 简介4.2 系统调用 五、UGO规则源码分析参考资料 一、安全简介 计算机系统应对安全挑战的办法大致有四种&#xff1a;隔离、控制、…...

解决Windows系统win+shift+s截图快捷键失效问题

文章目录 打开任务管理器找到Windows资源管理器&#xff0c;选择重新启动 打开任务管理器 按“Win R”打开&#xff1a; 输入taskmgr.exe&#xff0c;运行&#xff0c;即可打开任务管理器&#xff1a; 找到Windows资源管理器&#xff0c;选择重新启动 点击右下角的“重新启…...

Excel 快速填充

文章目录 利用快速填充进行提取数据利用快速填充进行拆分重组 2013 及以上版本才有的功能. 利用快速填充进行提取数据 有一列的数据已有, 需要提取部分数据到另一列, 只需要输入部分内容, 后面内容可以自动显示, 按下回车即可快速填充. 只要前面手动输入的内容没有错得太离谱…...

OPENCV图像和视频处理

图像基本操作&#xff1a; Opencv图像处理&#xff08;全&#xff09;_胖墩会武术的博客-CSDN博客 import cv2 import matplotlib.pyplot as plt import numpy as npimgcv2.imread(1.jpg) #图像的显示 cv2.imshow(image,img) cv2.waitKey(0) …...

QDir实践

现在有多个文件&#xff0c;路径为&#xff1a; a\xxx\kmd_config\c.json 其中xxx是变量 startcalc,,,,,, 目标&#xff1a; 访问每一个json文件 实例&#xff1a; QString app_path QApplication::applicationDirPath() "/app";QDir dir(app_path);QStringLi…...

网络通信三要素

三要素概述 IP地址&#xff1a;设备在网络中的地址&#xff0c;是唯一的标识。 端口&#xff1a;应用程序在设备中唯一的标识。 协议: 数据在网络中传输的规则&#xff0c;常见的协议有UDP协议和TCP协议。 网络通信过程 A程序通过IP和端口连接到到B程序&#xff0c;再互…...

2023年中国渔业研究报告

第一章 行业概况 1.1 定义 渔业&#xff0c;作为全球经济的重要支柱之一&#xff0c;其核心活动包括捕捞、水产养殖、产品加工与销售等。其不仅是食物安全的重要保障&#xff0c;还是许多沿海和内陆地区经济发展的重要动力。 首先&#xff0c;捕捞活动是渔业的基础。通过海洋…...

python字符串中的\“

data {"text": "\"abc\""} print(data) # {"text": ""abc""}从结果可以看到并没有出现反斜杠&#xff0c;反斜杠与双引号作为一个整体&#xff0c;转义为了一个双引号&#xff0c;如果要在字符串中出现反斜杠&am…...

Elasticsearch 分片内部原理—使文本可被搜索、动态更新索引

目录 一、使文本可被搜索 不变性 二、动态更新索引 删除和更新 一、使文本可被搜索 必须解决的第一个挑战是如何使文本可被搜索。 传统的数据库每个字段存储单个值&#xff0c;但这对全文检索并不够。文本字段中的每个单词需要被搜索&#xff0c;对数据库意味着需要单个字…...

lvgl 界面管理器

lv_scr_mgr lvgl 界面管理器 适配 lvgl 8.3 降低界面之间的耦合使用较小的内存&#xff0c;界面切换后会自动释放内存内存泄漏检测 使用方法 在lv_scr_mgr_port.h 中创建一个枚举&#xff0c;用于界面ID为每个界面创建一个页面管理器句柄将界面句柄添加到 lv_scr_mgr_por…...

一篇文章让你了解“JWT“

一.JWT简介 1.概念 JWT (JSON Web Token) 是一种用于在网络上安全传输信息的开放标准&#xff08;RFC 7519&#xff09;。它是一种紧凑且自包含的方式&#xff0c;用于在不同组件之间传递信息&#xff0c;通常用于身份验证和授权目的。JWT 是以 JSON 格式编码的令牌&#xff…...

HJ73 计算日期到天数转换

HJ73 计算日期到天数转换 int main() {int year, month, day;cin >> year >> month >> day;int monthDays[13] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };int nday monthDays[month - 1] day;if (month > 2 &&((year…...

Unity实现设计模式——适配器模式

Unity实现设计模式——适配器模式 适配器模式又称为变压器模式、包装模式&#xff08;Wrapper&#xff09; 将一个类的接口变换成客户端所期待的另一种接口&#xff0c;从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。 在一个在役的项目期望在原有接口的基础…...

【2023年11月第四版教材】专题1 - 计算题考点汇总 (合集篇)

专题1 - 计算题考点汇总 (合集篇&#xff09; 1 进度类1.1 PERT三点估算1.1.1 β分布1.1.2 三角分布 1.2 单代号网络图1.2.1 画图1.2.2 找关键路径1.2.3 计算总工期1.2.4 总时差1.2.5 自由时差1.2.6 工期压缩 1.3 双代号网络图1.4 双代号时标网络图1.4.1 画图1.4.2 找关键路径1…...

系统架构设计:17 论信息系统的安全性与保密性设计

目录 一 信息安全基础 1 信息安全的基本要素 2 信息安全的范围 3 网络安全...

使用EasyDarwin+ffmpeg+EasyPlayerPro完成rtsp的推流操作和拉流操作

本文分享在做视频类测试过程中所用到的工具EasyDarwinffmpegEasyPlayerPro 首先说一下EasyDarwin,简单来讲&#xff0c;它就是个推流和拉流及系统消耗的监测软件&#xff0c;具体使用方法我会写在下方。 EasyDarwin 1、解压下载好的EasyDarwin压缩包&#xff0c;并找到EasyD…...

FPGA project : flash_secter_erase

flash的指定扇区擦除实验。 先发写指令&#xff0c;再进入写锁存周期等待500ns&#xff0c;进入写扇区擦除指令&#xff0c;然后写扇区地址&#xff0c;页地址&#xff0c;字节地址。即可完成扇区擦除。 模块框图&#xff1a; 时序图&#xff1a; 代码&#xff1a; module…...

HarmonyOS/OpenHarmony原生应用-ArkTS万能卡片组件Radio

单选框&#xff0c;提供相应的用户交互选择项。该组件从API Version 8开始支持。无子组件。 一、接口 Radio(options: {value: string, group: string}) 从API version 9开始&#xff0c;该接口支持在ArkTS卡片中使用。 参数: 二、属性 除支持通用属性外&#xff0c;还支持以…...

python opencv 深度学习 指纹识别算法实现 计算机竞赛

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python opencv 深度学习 指纹识别算法实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;4分创新点&#xff1a;4分 该项目较为新颖…...

一图看懂CodeArts Inspector 三大特性,带你玩转漏洞管理服务

华为云漏洞管理服务CodeArts Inspector是面向软件研发和服务运维提供的一站式漏洞管理能力&#xff0c;通过持续评估系统和应用等资产&#xff0c;内置风险量化管理和在线风险分析处置能力&#xff0c;帮助组织快速感应和响应漏洞&#xff0c;并及时有效地完成漏洞修复工作&…...

【Mysql】Mysql的启动选项和系统变量(二)

概述 在Mysql的设置项中一般都有各自的默认值&#xff0c;比方说mysql 5.7服务器端允许同时连入的客户端的默认数量是 151 &#xff0c;表的默认存储引擎是 InnoDB &#xff0c;我们可以在程序启动的时候去修改这些默认值&#xff0c;对于这种在程序启动时指定的设置项也称之为…...

FPGA project : flash_read

实验目标&#xff1a; flash的普通读指令&#xff0c;在指定地址开始读。可以更改地址与读的数据个数。 先发送读指令扇区地址页地址字节地址。 然后读数据。再把读到的串行数据转化为8bit的数据&#xff0c;存入fifo。 然后读出FIFO中数据&#xff0c;通过uart_tx模块发送…...

nnunetv2训练报错 ValueError: mmap length is greater than file size

目录 报错解决办法 报错 笔者在使用 nnunetv2 进行 KiTS19肾脏肿瘤分割实验的训练步骤中 使用 2d 和3d_lowres 训练都没有问题 nnUNetv2_train 40 2d 0nnUNetv2_train 40 3d_lowres 0但是使用 3d_cascade_fullres 和 3d_fullres 训练 nnUNetv2_train 40 3d_cascade_fullres …...

React知识点系列(2)-每天10个小知识

目录 1. 如何优化 React 应用的性能&#xff1f;你用过哪些性能分析工具&#xff1f;2. 在 React 中&#xff0c;什么是 Context API&#xff1f;你在什么场景下会使用它&#xff1f;3. 你能解释一下什么是 React Fiber 吗&#xff1f;4. 在项目中&#xff0c;你是否使用过 Rea…...

AutoGPT:让 AI 帮你完成任务事情 | 开源日报 No.54

Significant-Gravitas/AutoGPT Stars: 150.4k License: MIT AutoGPT 是开源 AI 代理生态系统的核心工具包。它采用模块化和可扩展的框架&#xff0c;使您能够专注于以下方面&#xff1a; 构建 - 为惊人之作打下基础。测试 - 将您的代理调整到完美状态。查看 - 观察进展成果呈…...

USB 转串口芯片 CH340

目录 1、概述 2、特点 3、封装 4、引脚 6、参数 6.1 绝对最大值&#xff08;临界或者超过绝对最大值将可能导致芯片工作不正常甚至损坏&#xff09; 6.2 电气参数&#xff08;测试条件&#xff1a;TA25℃&#xff0c;VCC5V&#xff0c;不包括连接 USB 总线的引脚&…...

Day 05 python学习笔记

循环 应用&#xff1a;循环轮播图 最基础、最核心 循环&#xff1a;周而复始&#xff0c;谓之循环 (为了代码尽量不要重复) while循环 while的格式 索引定义 while 表达式&#xff08;只要结果为布尔值即可&#xff09;&#xff1a; 循环体 通过条件的不断变化&#xff0c;从…...

Python如何17行代码画一个爱心

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…...

生产环境中常用Linux命令

太简单的我就不讲解啦,浪费时间,直接将生产中常用的 文章目录 1.总纲2.整机 top3.CPU vmstat3. 内存 free4. 硬盘: df5. 磁盘IO iostat6. 网络IO ifstat7: 内存过高的情景排查 1.总纲 整机:topcpu:vmstat内存:free硬盘:df磁盘io: iostat网络io:ifstat 2.整机 top 首先们要查…...

【使用 TensorFlow 2】03/3 创建自定义损失函数

一、说明 TensorFlow 2发布已经接近5年时间&#xff0c;不仅继承了Keras快速上手和易于使用的特性&#xff0c;同时还扩展了原有Keras所不支持的分布式训练的特性。3大设计原则&#xff1a;简化概念&#xff0c;海纳百川&#xff0c;构建生态.这是本系列的第三部分&#xff0c;…...