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

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

本文内容参考:

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_CREDENTIALS
	atomic_t	subscribers;	/* number of processes subscribed */
	void		*put_addr;
	unsigned	magic;
#define CRED_MAGIC	0x43736564
#define CRED_MAGIC_DEAD	0x44656144
#endif
	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_KEYS
	unsigned 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_SECURITY
	void		*security;	/* LSM security */
#endif
	struct 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系统调用进行知识补强。没有讲完,本回继续讲解这一知识点,力求讲清楚、讲透彻。

知识补强 —— setuid系统调用

再来回顾一下setuid系统调用的功能细节。

上一回给出了一个setuid系统调用的应用例程,用以理解和验证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;

	//获取并打印初始 UID
	if (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或suid
		target_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 1
		//尝试设置为一个无效的UID(比如一个不存在的或不属于我的UID)
		//这通常会失败
		target_uid = 9999; //假设这是一个无效的或不属于当前用户的UID
 		printf("\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;
}

上一回开始讲解setuid在不同权限下的行为。首先讲解了调用者是特权用户(root)的情况,本回来看其余情况。

(2)以普通用户(文件所有者)执行程序

实际命令及结果如下:

如果进程没有超级用户特权,但参数uid等于实际用户ID(real UID)或保存的设置用户ID(saved UID),则setuid只将有效用户ID(effective UID)设置为uid,不改变实际用户ID和保存的设置用户ID。

对应的代码片段为:

	else
	{
		printf("\nRunning as a REGULAR USER (UID: %d)\n", getuid());

		printf("Before setuid.\n");
		show_curr_uids();

		//作为普通用户,只能设置为自己的ruid或suid
		target_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 1
		//尝试设置为一个无效的UID(比如一个不存在的或不属于我的UID)
		//这通常会失败
		target_uid = 9999; //假设这是一个无效的或不属于当前用户的UID
 		printf("\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
	}

这就对应的是setuid系统调用的具体行为(功能细节)中的:

setuid系统调用的其余功能细节,下一回进行解析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝天居士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值