Linux-进程权限

{% note default %}
Linux进程数据结构(中)- 进程树和进程权限

修改文件或目录权限

首先要了解的是,像以下这种

-rwxr-x--x 1 song song  8816 6月		2 18:25 aa.out
是通过ls -l aa.out命令打印出来的
我们这里关注前面的-rwxr-x--x,第一个-代表的aa.out这个文件是一个普通文件,当然还有其他比如d(目录)什么的
后面的9位rwxr-x--x,分别代表的是文件所有者对这个文件的权限,所有者所在组对这个文件的权限,其他用户对这个文件的权限
如果用二进制表示的话,那么有这个权限就置1,三位二进制数就可以表示0~7。

因此,如果用chmod命令修改该文件的具体权限的话,比如

chmod 000 aa.out

我们再看一下aa.out的权限信息

---------- 1 song song  8816 6月		2 18:25 aa.out

相当于权限全部被置为0,任何人都没有权限了,这就是一般的文件的权限
当然,chmod命令来修改命令还有其他方式
注意逗号

chmod u=rwx,g=rx,o=rx abc //同上u=用户权限,g=组权限,o=不同组其他用户权限

chmod u-x,g+w abc //给abc去除用户执行的权限,增加组写的权限

chmod a+r abc //给所有用户添加读的权限

其他还有重要的权限位:

  • setuid位:SUID位。这个是有效用户位。下面会讲
  • stick bit(粘滞位)
    有效用户位:对一个属主为root的可执行文件,如果设置了SUID位,则其他所有普通用户都将可以以root身份运行该文件,获取相应的系统资源。
    setuid,setuid的作用是让执行该命令的用户以该命令拥有者的权限去执行,比如普通用户执行passwd时会拥有root的权限,这样就可以修改/etc/passwd这个文件了。它的标志为:s,会出现在x的地方,例:-rwsr-xr-x 。而setgid的意思和它是一样的,即让执行文件的用户以该文件所属组的权限去执行。

stick bit(粘滞位):
我们知道/tmp是系统的临时文件目录,所有的用户在该目录下拥有所有的权限,也就是说在该目录下可以任意创建、修改、删除文件,那如果用户A在该目录下创建了一个文件,用户B将该文件删除了,这种情况我们是不能允许的。为了达到该目的,就出现了stick bit(粘滞位)的概念。它是针对目录来说的,如果该目录设置了stick bit(粘滞位),则该目录下的文件除了该文件的创建者和root用户可以删除和修改/tmp目录下的stuff,别的用户均不能动别人的,这就是粘滞位的作用。标志为t

进程的运行统计信息

u64				utime;//⽤⼾态消耗的CPU时间
u64				stime;//内核态消耗的CPU时间
unsigned long	nvcsw;//⾃愿(voluntary)上下⽂切换计数
unsigned long	nivcsw;//⾮⾃愿(involuntary)上下⽂切换计数
u64				start_time;//进程启动时间,不包含睡眠时间
u64				real_start_time;//进程启动时间,包含睡眠时间

进程亲缘关系

进程维护一个进程树

struct	task_struct_rcu	*real_parent;	/*	real	parent	process	*/
struct	task_struct_rcu	*parent;		/*	recipient	of	SIGCHLD,	wait4()	reports	*/
struct	list_head	children;				/*	list	of	my	children	*/
struct	list_head	sibling;				/*	linkage	in	my	parent's	children	list	*/

parent:指当前进程的父进程,该进程终止时,要向其父进程发送信号
children:指当前进程的子进程,指向链表的头部,链表中所有元素都是它的子进程
sibling:用于把当前进程插入到兄弟链表中

进程权限

Linux中,对进程权限的定义:

/* Objective and real subjective task credentials (COW): */
const struct cred_rcu *real_cred;
/* Effective (overridable) subjective task credentials (COW): */
const struct cred_rcu *cred;

关于权限,无非是两种情况,一种是“谁能操作我”,另一种就是“我能操作谁”。
Objective的中文意思就是“目标,目的”,subjective的中文意思是“主观的”。
这里有点绕,不好理解,我个人这么理解。
从subjective上来说,我作为个体,我主观的去想要操作某些对象。
那么这个subjective(cred)指的就是我能操作的哪些对象
而Objective呢,是我自身作为一个其他对象的目标,那么也就是谁能操作我
real_cred指的就是哪些对象能够操作我。

“操作”就是一个对象对另外一个对象进行某些动作,那么在动作实施的时候,就应该审核权限。
这里有意思的地方就是,上面讲过进程权限是两个部分,一个是“谁能操作我”,一个是“我能操作谁”。
那么审核权限的时候,就应该审核两边的权限一不一致(有待进一步理解)。

下面给出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	*/
......
		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	*/
......
}	__randomize_layout;

uid/gid就是实际的用户(组)id。
euid/egid,注释是effective user/group id,就是有效的用户(组)id,这里有效的,是指起作用的,一般就看这个权限。
fsuid和fsgid,也就是filesystem user/group id。这个是对文件操作会审核的权限。

前面讲过文件权限,这里我们要讲的是进程权限。
什么意思呢?前面我们还说过,进程权限就是两种情况:谁能操作我,我能操作谁。
这么去理解,不管怎么样,还是得看我本身的权限够不够。
那产生了一个进程,对应的也有了这个进程的权限。
我们不妨把这个进程看作是一个对象A,因为正在运行的进程也可能去操作其他对象B。
那我们怎么知道这个进程A有没有操作B的权限呢。
所以啊,进程权限里面的uid/gid、euid/egid、fsuid和fsgid就起了作用了。
你会发现,这些uid指的就是用户id,哪个用户启动了这个进程,那么这个进程的权限(这些uid)就是这个用户id。
那我进程A要操作B,是不是就要看具体哪个用户有没有这个权限呢?
用户有没有权限访问文件,你想到了什么?对!
就是文件权限。(当然也有进程权限,只是目前我没有探索清楚)
追根究底,我们又回到了文件权限,所以啊:
那9位,真的挺强大的。

继续讲,这里我们主要看euid/egid,就是有效用户ID,相信你也知道了,如果这个有效用户ID对应的用户没有操作对象B的相应权限。
那么对应的,进程A是不是就没有操作对象B的权限?

这个时候,我们怎么才能让A有这个权限呢?
当然,设置以下B的文件权限位,让A的有效用户ID能够访问就行了。

但是,一般情况是:
{% note defalut %}
创建进程A,前提是执行一个可执行文件file,这个可执行文件的所有者是R
当用户T去执行该可执行文件file时(会检查T有没有执行权限),创建进程,这个时候如果file没有设置SUID位的话
那么进程A的uid、euid、fsuid等都会是T的id,这个时候如果进程A要去操作同样属于R的另一个对象B(没有设置T能够操作的权限)的时候,就无法成功。
因为这个时候进程A的有效用户ID是T,T没有对B的操作相应权限。
{% endnote %}
这个时候怎么办呢?解决方法如下:
这个时候我们就可以设置可执行文件file的setuid位(SUID位)来让创建的进程A的euid、fsuid是用户R的ID,这个时候进程A就可以操作同样属于R的对象B了,也就是说,别的用户创建的进程A也能操作对象B。这是因为:上面讲过

setuid的作用是让执行该命令的用户以该命令拥有者的权限去执行

另外还可以通过setuid或seteuid函数(系统调用)的方式来设置uid,具体见下(出自饶超勋
stevenrao.blog.chinaunix.net):

int main(int argc, char *argv[])
{
    if( setuid(503) < 0) perror ("setuid error");
    while(1)sleep(1);
}
   $>ll
   -rwxr-xr-x. 1 test1 test 6780 Sep 16 15:32 a.out 
   使用root用户执行
   $>./a.out
   查看状态,所有uid都变成test2用户。
   503   503   503   503  3592 a.out

   把代码中setuid改成seteuid函数,会把euid和fuid改成test2用户
   0   503     0   503  3614 a.out
   
   把代码中setuid改成setfsuid函数,会把fuid改成test2用户
   0     0     0   503  3636 a.out

   当把代码改成下面样子
if( seteuid(503) < 0) perror ("seteuid error");
if( setfsuid(504) < 0) perror ("setfsuid error");
while(1)sleep(1);
   或者
if( setfsuid(504) < 0) perror ("setfsuid error");
if( setfeuid(503) < 0) perror ("seteuid error");
while(1)sleep(1);
   用root用户执行,得到都是一样的结果
   0   503     0   503  3614 a.out  
   
   到了这里我来总结一下:1、setuid和seteuid是有区别的,setuid是永久的放弃root用户权限,转让给非root用户后,无法再restore到root用户,seteuid是临时放弃root用户权限,可以通过seteuid(0),restore到root权限。这点应该是总所周知的特点,本文就不举例子演示。2、seteuid 会同时改变euid和fuid都为设置的euid值。3、root用户可以通过调用setxxuid 来改变权限用户。非root用户是无法改变和转让权限用户。

当然还有函数setfsuid什么的,慢慢摸索

最后讲一下权限的继承,:
当使用fork子进程的时候,子进程全部继承父进程四个uid,和父进程uid相同。
当使用exec系列函数时候,会把suid置为euid。
{% note warning %}
个人总结一下:个人理解,进程权限其实理解成用户权限的化身更好一些,就像是用户的化身一样去执行各种操作,而在操作对象时,要检查这个化身有没有相应的权限。

Linux还有另一个机制就是capabilities。后期补充。

//进程数据结构(下)用户态函数栈和内核栈

进程执行过程中,发生系统调用怎么办

在程序执行过程中,一旦调用到系统调用,就需要进入内核继续执
行。那如何将用户态的执行和内核态的执行串起来呢?

就需要以下两个重要的成员变量:

struct	thread_info		thread_info;
void		*stack;

用户态函数栈较好理解,在CSAPP就有涉及,里面讲的是64位的。
内核栈较为复杂。后期补充。

调度
后续更新或另起博文。


posted @ 2022-01-06 23:51  Ryan~~~~  阅读(497)  评论(0)    收藏  举报