每个进程都有一个实际用户标识符和一个实际组标识符,它们永远是启动该进程之用户的用户标识符和组标识符。
进程的有效用户标识符和有效组标识符也许更重要些,它们被用来确定一个用户能否访问某个确定的文件。在通常情况下,它们与实际用户标识符和实际组标识符是一致的。
有几个系统调用可以用来得到进程的用户标识符和组标识符,详见下列程序:
/* 取进程的实际用户标识符 */
uid=getuid();
/* 取进程的有效用户标识符 */
euid=geteuid();
/* 取进程的实际组标识符 */
gid=getgid();
/* 取进程的有效组标识符 */
egid=getegid();
另外,还有两个系统调用可以用来设置进程的有效用户标识符和有效组标识符,它们的使用格式如下:
/* 设定进程的有效用户标识符 */
status=setuid(newuid);
/* 设定进程的有效组标识符 */
status=setgid(newid)
通过这两个系统调用,进程可以改变自己的标识符,进而改变自己的权限(因为Linux中权限是通过标识符来判断的)。比如一个root 建立的进程可以用这种方法放弃一部分的root 权限而只保留工作所需的权限。这样可以提高系统的安全性。
<span style="font-size:14px;">#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<fcntl.h> #include<stdlib.h> int main(int argc,char* argv[]) { int fd; fd = fork(); switch(fd){ case -1:{ perror("fork"); exit(-1); } break; case 0: { //开始进程用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //设置用户id为1000,改变用户权限 setuid(1000); //改变后的用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //root用户才能查看shadow文件,测试权限是否改变 execlp("cat","cat","/etc/shadow",NULL); } break; default: { //开始进程用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //root用户才能查看shadow文件 execlp("cat","cat","/etc/shadow",NULL); } break; } return 0; } </span>
在root权限下运行结果:
[[email protected] file]# ./a.out
uid=0,euid=0
uid=1000,euid=1000
cat: /etc/shadow: 权限不够
uid=0,euid=0
root:$$MG0tTs8yWCPoKMrG$tVrZQKQ6IK52ucaSGfHIMKdVHB7zP.rpqD5GO/1w07eYQj0Jgue9S/UijUtyYYQa9Irm2vwj7r.DwaY.5IXIp0:15195:0:99999:7:::
bin:*:14789:0:99999:7:::
但是需要注意的是,一旦root 进程通过这种方式放弃了root 特权,将无法再通过setuid()调用的方式重新获得root权,因为一个非root 标识符的进程是无法设定root 标识符的。这时可以使用Linux 的另外两个系统调用seteuid()和setegid()。其调用方式和前两个完全相同。但是它们是根据进程程序文件的标识符来判断设定的。因此,一个root 的程序文件在任何时候都可以将自己重新seteuid()为root。
<span style="font-size:14px;">#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<fcntl.h> #include<stdlib.h> int main(int argc,char* argv[]){ //开始进程用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //设置用户id为1000,改变用户权限 setuid(1000); //改变后的用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //设置用户id为0,改变用户权限为root if(-1 == setuid(0)){ perror("setuid to root"); exit(-1); } //改变后的用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); return 0; } </span>
root下运行结果:
[[email protected] file]# ./a.out
uid=0,euid=0
uid=1000,euid=1000
setuid to root: Operation not permitted
<span style="font-size:14px;">#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<fcntl.h> #include<stdlib.h> int main(int argc,char* argv[]){ //开始进程用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //设置用户id为1000,改变用户权限 setuid(1000); //改变后的用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //设置用户id为1000,改变用户权限为自己 if(-1 == setuid(1000)){ perror("setuid to root"); exit(-1); } //改变后的用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); return 0; }</span>
运行结果:
以上例子说明不是超级用户所引用的进程,只能把它的有效用户表示符和有效组标识符重新设置成其实际用户标识符和实际组标识符。超级用户所引用的进程就可以自由进行其有效用户标识符和有效组标识符的设置。
以上使用setuid()永久改变权限,现在使用seteuid()暂时改变用户权限:
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<fcntl.h> #include<stdlib.h> int main(int argc,char* argv[]){ //开始进程用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //设置有效用户id为1000,暂时改变用户权限 seteuid(1000); //改变后的用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //设置有效用户id为0,改变用户权限为自己 if(-1 == setuid(0)){ perror("setuid to root"); exit(-1); } //改变后的用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); return 0; }
运行结果:
#./a.out
uid=0,euid=0
uid=0,euid=1000
uid=0,euid=0
[[email protected] file]# ./a.out
uid=0,euid=0
uid=0,euid=1000
uid=0,euid=0[[email protected]
file]# ./a.out
uid=0,euid=0
uid=0,euid=1000
uid=0,euid=0
[[email protected] file]# ./a.out
uid=0,euid=0
uid=1000,euid=1000
uid=1000,euid=1000[[email protected] file]# ./a.out
uid=0,euid=0
uid=1000,euid=1000
uid=1000,euid=1000[[email protected] file]# ./a.out
uid=0,euid=0
uid=1000,euid=1000
uid=1000,euid=1000[[email protected] file]# ./a.out
uid=0,euid=0
uid=1000,euid=1000
uid=1000,euid=1000[[email protected] file]# ./a.out
uid=0,euid=0
uid=1000,euid=1000
uid=1000,euid=1000
linux进程标识符详解1