Linux内核进程管理子系统有什么第四十六回 —— 进程主结构详解(42)

接前一篇文章:Linux内核进程管理子系统有什么第四十五回 —— 进程主结构详解(41)

 

本文内容参考:

Linux内核进程管理专题报告_linux rseq-CSDN博客

《趣谈Linux操作系统 核心原理篇:第三部分 进程管理》—— 刘超

《图解Linux内核 基于6.x》 —— 姜亚华 机械工业出版社

setuid系统调用及示例-CSDN博客

setuid函数解析 - HelloMarsMan - 博客园

特此致谢!

 

进程管理核心结构 —— task_struct

8. 进程权限相关成员

进程权限相关成员包括以下几个:

​​	/* 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;

这几个字段的描述如下:

上一回继续对于struct cred进行解析,本回仍然继续。为了便于理解和回顾,再次贴出struct cred的定义,在include/linux/cred.h中,如下:

/** 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 {atomic_t	usage;
#ifdef CONFIG_DEBUG_CREDENTIALSatomic_t	subscribers;	/* number of processes subscribed */void		*put_addr;unsigned	magic;
#define CRED_MAGIC	0x43736564
#define CRED_MAGIC_DEAD	0x44656144
#endifkuid_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;	/* LSM security */
#endifstruct user_struct *user;	/* real user ID subscription */struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */struct ucounts *ucounts;struct group_info *group_info;	/* supplementary groups for euid/fsgid *//* RCU deletion */union {int non_rcu;			/* Can we skip RCU deletion? */struct rcu_head	rcu;		/* RCU deletion hook */};
} __randomize_layout;

前边几回结合例程,讲解setuid在不同权限下的行为。分别讲解了:

(1)调用者是特权用户(root)的情况

(2)调用者是普通用户(程序文件拥有者)的情况

(3)调用者是普通用户(非程序文件拥有者)的情况。

上一回笔者说虽然setuid系统调用的各种情况都讲了,但并未讲到真正的精髓。本回就来讲讲这个精髓之处。为了便于理解和回顾,再次贴出例程代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#define _GNU_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>void show_curr_uids(void)
{int ret;uid_t real_uid;uid_t effective_uid;uid_t saved_uid;ret = getresuid(&real_uid, &effective_uid, &saved_uid);printf("Real uid: %d, Effective uid: %d\n", real_uid, effective_uid);printf("Current UIDs:\n");printf("\tReal uid: %d\n\tEffective uid: %d\n\tSaved uid: %d\n", getuid(), geteuid(), (getresuid(&real_uid, &effective_uid, &saved_uid) == 0) ? saved_uid : -1);
}int main(int argc, char *argv[])
{int ret;uid_t original_ruid, original_euid, original_suid;uid_t target_uid;//获取并打印初始 UIDif (getresuid(&original_ruid, &original_euid, &original_suid) != 0){perror("getresuid");exit(EXIT_FAILURE);}//检查是否以root权限运行if (geteuid() == 0){printf("\nRunning as ROOT (Privileged User)\n");printf("Before setuid.\n");show_curr_uids();//作为root,可以设置为任意有效的UID//这里我们尝试设置为'nobody' 用户 (通常UID 65534, 但请检查你的系统)target_uid = 65534;printf("\nAttempting to set UID to %d (usually 'nobody' user)...\n", target_uid);ret = setuid(target_uid);if (ret == 0){printf("setuid(%d) succeeded.\n", target_uid);printf("\nAfter setuid.\n");show_curr_uids();printf("Note: All UIDs (Real, Effective, Saved) are now %d.\n", target_uid);printf("The process is now running with 'nobody' privileges.\n");}else{perror("setuid");printf("Failed to set UID to %d.\n", target_uid);}}else{printf("\nRunning as a REGULAR USER (UID: %d)\n", getuid());printf("Before setuid.\n");show_curr_uids();//作为普通用户,只能设置为自己的ruid或suidtarget_uid = original_ruid; //选择设置为自己的真实UID (这不会改变任何东西)printf("\nAttempting to set UID to my Real UID (%d)...\n", target_uid);ret = setuid(target_uid);if (ret == 0){printf("setuid(%d) succeeded (as expected).\n", target_uid);printf("\nAfter setuid.\n");show_curr_uids();}else{perror("setuid");printf("Failed to set UID to %d.\n", target_uid);}#if 0//尝试设置为一个无效的UID(比如一个不存在的或不属于我的UID)//这通常会失败target_uid = 9999; //假设这是一个无效的或不属于当前用户的UIDprintf("\nAttempting to set UID to an invalid/different UID (%d)...\n", target_uid);ret = setuid(target_uid);if (ret == -1){if (errno == EPERM){printf("setuid(%d) failed with EPERM (Operation not permitted) - as expected for a regular user.\n", target_uid);printf("This is because %d is not my Real UID (%d) or Saved Set-UID (%d).\n", target_uid, original_ruid, original_suid);}else{perror("setuid");printf("Failed to set UID to %d.\n", target_uid);}printf("After failed setuid.\n");show_curr_uids();}else{printf("setuid(%d) unexpectedly succeeded.\n", target_uid);printf("After unexpected setuid.\n");show_curr_uids();}
#endif}return 0;
}

这次只关注代码中else分支的前半段,也就是:

	//检查是否以root权限运行if (geteuid() == 0){……}else{printf("\nRunning as a REGULAR USER (UID: %d)\n", getuid());printf("Before setuid.\n");show_curr_uids();//作为普通用户,只能设置为自己的ruid或suidtarget_uid = original_ruid; //选择设置为自己的真实UID (这不会改变任何东西)printf("\nAttempting to set UID to my Real UID (%d)...\n", target_uid);ret = setuid(target_uid);if (ret == 0){printf("setuid(%d) succeeded (as expected).\n", target_uid);printf("\nAfter setuid.\n");show_curr_uids();}else{perror("setuid");printf("Failed to set UID to %d.\n", target_uid);}#if 0……
#endif}

这次要做的实验步骤如下:

1)首先,通过chmod u+s /tmp/setuid_example命令,对setuid_example添加上SUID权限

实际命令及结果如下:

2)使用shiyan用户(UID为1001)来运行setuid_example程序

这个程序的实际用户ID(real UID)为1001,而有效用户ID(effective UID)和保存的设置用户ID(saved UID)都为1000。接着,在这个程序中调用setuid函数,传入的参数为1001,大家可以猜想到,最终应该只有有效用户ID受到影响(会从1000变为1001),而保存的设置用户ID保持不变。

实测结果如下:

实测结果与预期一致。

再来对比一下没有使用chmod u+s /tmp/setuid_example命令之前的结果:

也对比一下示例代码中将target_uid(uid)设置为1000(用户ph的UID)的结果:

虽然上面的示例在运行时调用setuid,但setuid系统调用的强大之处还体现在可执行文件的SUID位上。这就要和chmod u+s命令结合起来,共同起作用了。

当将一个可执行文件的SUID位设置为1时(如通过chmod u+s xxx),会发生以下情况:

无论哪个用户执行这个文件,该进程启动时的有效用户ID(EUID)均会被设置为该文件所有者的用户ID。这使得普通用户可以运行一个具有程序文件所有者权限的程序。

这一点从上边的结果就可以看出来,没有执行chmod u+s /tmp/setuid_example之前,Effective UID是1001,也就是用户shiyan;

执行chmod u+s /tmp/setuid_example后,Effective UID是1000,也就是setuid_example的属主ph了。

为了更好地理解setuid和chmod u+s,这里再给出一个例子:

至此,struct cred的前4组字段就解析完了。下一回继续解析后续字段。

 

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

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

相关文章

Linux网络连接不上?NetworkManager提示“device not managed“!

#操作系统 #Linux #NetworkManager适用环境kylin v10Centos 8Redhat 8一、故障现象在CentOS/RHEL(同样适用于kylin v10&#xff09;系统中&#xff0c;管理员执行 nmcli connection up ens160 命令尝试激活名为 ens160 的网络连接时&#xff0c;遇到以下错误&#xff1a;[roo…

【系统分析师】第2章-基础知识:数学与工程基础(核心总结)

更多内容请见: 备考系统分析师-专栏介绍和目录 文章目录 一、数学统计基础 1.1 概率论基础 1.2 数理统计基础 1.3 常用统计分析方法 二、图论应用 2.1 基本概念 2.2 核心算法与应用 三、预测与决策 3.1 预测方法 3.2 决策方法 四、数学建模 4.1 建模过程 4.2 常用模型类型 五、…

StrUtil.isBlank()

这段代码是一个条件判断&#xff0c;用于检查变量 shopJson 是否为空或空白&#xff0c;如果是&#xff0c;就直接返回 null。我们来逐句讲解&#xff1a;原始代码&#xff1a; if(StrUtil.isBlank(shopJson)) {// 3.存在&#xff0c;直接返回return null; }逐句解释&#xff1…

mysql 回表查询(二次查询,如何检查,如何规避)

h5打开以查看 “回表查询”通常发生在使用二级索引&#xff08;Secondary Index&#xff09;的查询中。当查询所需的数据列并不全部包含在二级索引中时&#xff0c;即使使用了索引&#xff0c;MySQL 也需要根据索引记录中的主键值&#xff0c;回到聚簇索引&#xff08;Cluster…

深度学习(二):神经元与神经网络

在人工智能的浪潮中&#xff0c;神经网络&#xff08;Neural Networks&#xff09;无疑是驱动核心技术的引擎&#xff0c;它赋予了计算机前所未有的学习和识别能力。而这一切的起点&#xff0c;是受到生物大脑中基本单元——神经元&#xff08;Neurons&#xff09;的深刻启发。…

JavaScript 行为型设计模式详解

1. 观察者模式1.1. 使用场景观察者模式用于对象间的一对多依赖关系&#xff0c;当一个对象的状态发生变化时&#xff0c;所有依赖于它的对象都能收到通知并自动更新。常用于事件处理、通知系统。在前端中&#xff0c;观察者模式用于实现事件监听、数据绑定等功能。1.2. 代码实现…

指令查找表LUT

本文整理自22. FlexSPI—读写外部SPI NorFlash — [野火]i.MX RT库开发实战指南——基于i.MXRT1052 文档 用作个人学习和分享 指令查找表LUT 访问FLASH存储器通常包含一些读写功能的的控制指令&#xff0c;主控设备可通过这些指令访问FLASH存储器。 为了适应这种需求&#…

uv使用指南

&#x1f680; Python 打包工具 UV 使用指南 UV 是一个用 Rust 编写的极速 Python 包管理器和解析器&#xff0c;旨在成为 pip、pip-tools、virtualenv 等工具的单一替代方案。 &#x1f4cb; 目录 核心概念与设计哲学安装与配置基础使用方法项目管理与工作流高级功能与技巧…

安卓学习 之 图片控件和图片按钮

今天学习的是ImageView 和 ImageButton这两个控件还是比较简单的&#xff1a;先来看看最后的样式图片吧&#xff1a;从图片中可以看到ImageView中的图片要大很多&#xff0c;这是因为中的ImageView中的图片跟ImageView控件的大小而自动调整。Imag…

动态规划-学习笔记

这是一份动态规划&#xff08;Dynamic Programming, DP&#xff09;完整学习笔记。笔记将从一星难度&#xff08;入门&#xff09;到五星难度&#xff08;进阶&#xff09;&#xff0c;循序渐进&#xff0c;涵盖核心思想、经典模型和解题方法论。 本来打算今天更新背包问题的题…

Linux 可信启动深度解析:从UEFI到操作系统的信任链

文章目录引言一、 可信根基&#xff1a;TPM与核心概念1.1 什么是“度量” (Measurement)&#xff1f;1.2 信任链与TPM PCR二、 阶段一&#xff1a;固件的可信启动 (UEFI)2.1 引导的起点&#xff1a;从SEC到DXE的初始化2.2 引导设备选择 (BDS)&#xff1a;UEFI如何找到GRUB2.3 S…

61-python中面向对象三大特性

前言&#xff1a; 面向对象编程&#xff0c;是许多编程语言都支持的一种编程思想。简单理解是&#xff1a;基于模板&#xff08;类&#xff09;去创建实体&#xff08;对象&#xff09;&#xff0c; 使用对象完成功能开发。面向对象包含三大主要特性&#xff1a; 封装 继承 多态…

BP-Adaboost模型

BP-Adaboost模型是一种将BP神经网络作为弱分类器的集成学习框架&#xff0c;通过AdaBoost算法动态调整样本权重和模型权重&#xff0c;显著提升预测精度和泛化能力。一、模型架构与工作原理 1. 基础框架 弱分类器单元&#xff1a;采用单隐藏层BP神经网络&#xff08;结构示例&a…

k230 +canMV+ LVGL控件 仿手表表盘触摸屏滚动、选中后弹窗效果完整示例程序

现在智能手表用的越来越多,其交互方式比较有特点,现在k230开发板上,基于LVGL(Light and Versatile Graphics Library)编写一个嵌入式GUI应用程序,使用LVGL配合触摸屏实现模仿智能手表的表盘滚动效果,实际效果如下: 程序使用LVGL图形库和MediaManager程序,创建带有触摸…

使用Vue.js和WebSocket打造实时库存仪表盘

大家好&#xff01;今天我将分享一个简单却强大的实时库存仪表盘项目&#xff0c;基于Vue.js和WebSocket技术。这个项目适合初学者学习前端实时数据处理&#xff0c;也能为你的技术博客或作品集增添亮点&#xff01;通过这个教程&#xff0c;你将学会如何使用WebSocket实现实时…

leecode100——接雨水

题目 双指针 思路1 使用参数存储从左往右&#xff08;从右往左同理&#xff09;遍历时的最高的柱子&#xff0c; 然后移动左右的指针&#xff0c;每次移动左右指针中偏向小的&#xff0c; 如果当前指针指的柱子小于最高的柱子&#xff0c;就会存在接到水。 思路2 把水看作柱子&…

复古胶片风格街拍人像Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程复古胶片风格街拍人像 Lightroom 调色&#xff0c;通过模拟经典胶片相机的色彩科学&#xff0c;为现代数码照片注入怀旧韵味。这种调色手法注重低饱和度色彩、柔和的高光过渡和丰富的暗部细节&#xff0c;配合适度的颗粒感&#xff0c;营造出时光沉淀的质感。特别适合街…

Linux的gpio子系统

GPIO其实也是某个pin的功能之一。上一小节讲解了 pinctrl 子系统&#xff0c;pinctrl 子系统重点是设置 PIN(有的 SOC 叫做 PAD)的复用和电气属性&#xff0c;如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话&#xff0c;那么接下来就要用到 gpio 子系统了。gpio 子系统顾名思…

VC++ CPU指令集检测工具实现原理

&#x1f4c8; VC CPU指令集检测工具实现原理 例图&#xff1a;&#x1f9e0; 1. 核心原理&#xff1a;CPUID指令 // 使用CPUID指令获取CPU信息 int cpuInfo[4] { -1 }; __cpuid(cpuInfo, 0); // 调用CPUID指令 int nIds cpuInfo[0]; // 获取最大标准功能号CPUID指令工作流程…

大模型微调理论、实战:LLaMA-Factory、Unsloth

概述 微调&#xff0c;Fine-Tuning&#xff0c;简称FT&#xff0c;可理解为对LLM的定制&#xff0c;目的是增强专业领域知识&#xff0c;并优化特定任务的性能。通过在特定数据集上微调一个预训练模型&#xff0c;可实现&#xff1a; 更新知识&#xff1a;引入新的领域专属信…