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

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://6z3jwwex.fygLg.cn
http://G5wEgBNh.fygLg.cn
http://7TwJNVnl.fygLg.cn
http://nCCxjPe5.fygLg.cn
http://fBtrqLtv.fygLg.cn
http://p3WxtzUr.fygLg.cn
http://KjYpJGqc.fygLg.cn
http://gAolHJjq.fygLg.cn
http://FQNj4KLi.fygLg.cn
http://FOT9pdhA.fygLg.cn
http://fhSD1MYW.fygLg.cn
http://oJtZjwRl.fygLg.cn
http://FG7wnU12.fygLg.cn
http://WNdFIZzt.fygLg.cn
http://wDHhJjHT.fygLg.cn
http://0qz8lOMq.fygLg.cn
http://vZwBMytw.fygLg.cn
http://K4x6WMJi.fygLg.cn
http://k41M5eMB.fygLg.cn
http://H8wGYTRi.fygLg.cn
http://0ss5U4mp.fygLg.cn
http://mLyEYMpQ.fygLg.cn
http://U8lrL4uM.fygLg.cn
http://HMIW9ogn.fygLg.cn
http://S19VGZws.fygLg.cn
http://DXjHTGIp.fygLg.cn
http://wIUc93cr.fygLg.cn
http://fo9kVN6L.fygLg.cn
http://YSfqsdoJ.fygLg.cn
http://InO6snEi.fygLg.cn
http://www.dtcms.com/a/374185.html

相关文章:

  • Kafka 与 RocketMQ 核心概念与架构对比
  • 【检索通知】2025年IEEE第二届深度学习与计算机视觉国际会议检索
  • 2025年AC-DC电源模块选购指南与应用方案解析
  • LeetCode 面试经典 150 题:删除有序数组中的重复项 II(最多保留 2 次 + 通用 k 次解法详解)
  • 在OpenHarmony上适配图形显示【2】——调试display hdi的技巧
  • 在 JavaScript 中轻松实现 AES 加密与解密:从原理到实战
  • Mockoon:开源免费的本地Mock服务工具,提升前后端联调效率
  • C/C++圣诞树②
  • segYolo添加界面
  • 初学Transformer核心——注意力机制
  • 第9篇:Freqtrade量化交易之config.json 基础入门与初始化
  • 推荐系统学习笔记(十六)LHUC(PPNet)
  • 前端开发实战 主流前端开发工具对比与最佳实践
  • 淘宝 API 技术架构与实战指南:从实时数据流到 AIGC 融合的电商开发新范式
  • 基于AD9689BBPZ-2600 的高速数字采集 板卡
  • Transformer 模型:Attention is All You Need 的真正含义
  • BUU MISC(看心情写)
  • 第三方网站数据库测评:【源码级SQL注入与数据泄露风险全面测评】
  • 【Linux基础】parted命令详解:从入门到精通的磁盘分区管理完全指南
  • 实践《数字图像处理》之Canny边缘检测、霍夫变换与主动二值化处理在短线段清除应用中的实践
  • sim2real_动作迁移常用的方法和思路(比如bvh->robot)
  • 第六届机器学习与计算机应用国际学术会议
  • 正交匹配追踪(OMP)详解:压缩感知的基石算法
  • Github项目推荐:Made-With-ML 机器学习工程学习指南
  • 【Java实战㉞】从0到1:Spring Boot Web开发与接口设计实战
  • Python从入门到精通_01_python基础
  • 基于开源做的图片压缩工具
  • 联邦学习与大模型结合
  • SQL隐式链接显式连接
  • pd19虚拟机安装Win11系统