Linux进程的实际用户ID和有效用户ID

在Unix进程中涉及多个用户ID和用户组ID,包括如下:

1、实际用户ID和实际用户组ID:标识我是谁。也就是登录用户的uid和gid

     比如我的Linux以oyjb登录,在Linux运行的所有的命令的实际用户ID都是oyjb的uid,实际用户组ID都是oyjb的gid(可以用id命令查看)

2、有效用户ID和有效用户组ID:进程用来决定我们对资源的访问权限

       (1一般情况下,有效用户ID等于实际用户ID,有效用户组ID等于实际用户组ID

     (2)当设置-用户-ID(SUID)位设置,则有效用户ID等于文件的所有者的uid,而不是实际用户ID

     (3)同样,如果设置了设置-用户组-ID(SGID)位,则有效用户组ID等于文件所有者的gid,而不是实际用户组ID






在使用 setuid() 函数时会遇到 3 个关于 ID 的概念:

real user ID -- 真实用户 ID
effective user ID -- 有效用户 ID
saved set-user-ID -- 保存了的设置用户 ID

使用 chmod 改变该程序的有效权限位(suid):
chmod u+s  getuid.exe 
$ ls -l getuid.exe  
-rwsr-xr-x 1 beyes beyes 4775 Jun  9 15:38 getuid.exe



下面通过 setuid() 和 seteuid() 这两个函数来考察一下 saved set-user-ID (保存的设置用户ID)这个概念。

参考:https://code.csdn.net/snippets/503010

在使用 setuid() 时会遇到如下情况:
1. 若进程有 root 权限,则函数将实际用户 ID、有效用户 ID 设置为参数 uid  。见如下代码:
[html]   view plain  copy  在CODE上查看代码片  派生到我的代码片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <sys/types.h>  
  4. #include <unistd.h>  
  5.     
  6. void show_ids(void)  
  7. {  
  8.     printf ("real uid: %d\n", getuid());  
  9.     printf ("effective uid: %d\n", geteuid());  
  10. }  
  11.     
  12. int main(int argc, char *argv[])  
  13. {  
  14.         
  15.     int uid;  
  16.     show_ids();  
  17.     uid = atoi(argv[1]);  
  18.     
  19.     if (setuid (uid) < 0)   
  20.         perror ("setuid error");  
  21.           
  22.     show_ids();  
  23.     
  24.     return (0);  
  25. }  
下面使用 root 用户来运行上面的程序:
# ./setuid.exe 1001 
real uid: 0 
effective uid: 0 
real uid: 1001 
effective uid: 1001
由此可见,在 root 下,实际用户 ID 和有效用户 ID 均被设为 setuid() 的参数 uid 的值。

2. 若进程不具有 root 权限,那么普通用户使用 setuid() 时参数 uid 只能是自己的,没有权限设置别的数值,否则返回失败:
使用普通用户来运行:
$ ./setuid.exe 1001 
real uid: 1000 
effective uid: 1000 
setuid error: Operation not permitted 
real uid: 1000 
effective uid: 1000
由以上可以看出,只有超级用户进程才能更改实际用户 ID 。所以一个非特权用户进程不能通过 setuid 或 seteuid 得到特权用户权限。

这里考虑 su 这个程序,su 可以将普通用户切换到 root 用户。这是因为,su 被设置了 有效权限位:
# ll /bin/su  
-rwsr-xr-x 1 root root 29152 Feb 16 04:50 /bin/su
如上面所做实验描述的一样,普通用户在运行 su 时,它也就拥有了 root 的权限。

对于调用了 setuid() 函数的程序要格外小心,当进程的有效用户 ID 即 euid 是 root 用户时(设置了有效权限位),如果调用了 setuid() ,那么它会与它相关的所有 ID (real user ID, effective user ID,saved set-user-ID) 都变成它参数里所设的 ID,这样该进程就变成了普通用户进程,也就再也恢复不了 root 权限了。看下面代码:
[html]   view plain  copy  在CODE上查看代码片  派生到我的代码片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.     
  4. void show_ids (void)  
  5. {  
  6.         printf ("The real user ID is: %d\n", getuid());  
  7.         printf ("The effective user ID is :%d\n", geteuid());  
  8. }  
  9.     
  10. int main(void)  
  11. {  
  12.         const char *file = "/root/rootfile3.txt";     
  13.         setuid (0)  
  14.         show_ids();  
  15.         if (!unlink (file)) {  
  16.                 printf ("Ok, I am root, and I can delete the file which in /root directory.\n");  
  17.                 system ("echo hello world > /root/rootfile3.txt");  
  18.                 printf ("Now, drop the root privileges.\n");  
  19.                 if (setuid (1000) < 0) {  
  20.                         perror ("setuid");  
  21.                         exit (EXIT_FAILURE);  
  22.                 }  
  23.                 show_ids();  
  24.                 if (unlink (file) < 0) {  
  25.                         printf ("Ok, we have no privilege to delete rootfile.txt.\n");  
  26.                 }  
  27.                 printf ("try to regain root power again...\n");  
  28.                 if (seteuid (0)) {  
  29.                         perror ("seteuid");  
  30.                         show_ids();  
  31.                         exit (EXIT_FAILURE);  
  32.                 }  
  33.         }  
 
 我们使用 root 编译上面的程序,并运行 chmod u+s 给程序添加 suid 位,然后以普通用户来运行它: 
 
# ./getuid3 The real user ID is: 0The effective user ID is :0Ok, I am root, and I can delete the file which in /root directory.Now, drop the root privileges.The real user ID is: 1000The effective user ID is :1000Ok, we have no privilege to delete rootfile.txt.try to regain root power again...  seteuid: Operation not permittedThe real user ID is: 1000The effective user ID is :1000
由输出可见,在运行 setuid (1000) 函数时,我们还是具有 root 权限的,所以该函数会设置成功。正是因为有了 root 权限,所以 3 个 ID (真实用户ID,已保存用户ID,有效用户ID)都会被设置为 1000。所以在运行完 setuid(1000) 后,进程已经被降权为普通用户,此时想再  seteuid (0) 提高权限已经不可能。这里需要提到一点,对于 show_ids() 函数里,我们无法获取 保存的设置用户ID(saved set-user-ID),这是因为没有这种 API 。但是我们知道这个约定:当用户是 root 时,使用 setuid() 来修改 uid,这 3 个 ID 是会被同时都修改的。但是有没有办法,先使进程降权后在某些时候再恢复 root 权力呢?办法是使用 seteuid() 而不是 setuid() 。那 setuid() 和 seteuid() 有什么不同么?在 seteuid() 的 man 手册里提到:
seteuid() sets the effective user ID of the calling process.  Unprivileged user processes may only set the effective user ID to the real user ID, the effec‐tive user ID or the saved set-user-ID.
setedui() 用来设置调用进程的有效用户 ID。普通用户进程只能将 有效用户ID 设置为 实际用户ID,有效用户ID,保存的设置用户ID。这里比较重要的是, seteuid() 中的参数可以被设置为 保存的设置用户 ID  。保存的设置用户 ID 是这样的一种概念:它是从 exec 复制有效用户 ID 而得来的。具体的说,当我们从一个 shell 里执行一个外部命令时(这里就当做是执行上面的 getuid3 这个),如果该程序设置了用户ID位(有效权限位),那么在 exec 根据文件的用户ID设置了进程的有效用户 ID 后,就会将这个副本保存起来。简单的说,saved set-user-ID 保存了 有效用户ID 的值。比如对于 getuid3 这个程序,saved set-user-ID 保存的值就是 0 。据此,我们修改上面的 getuid3 程序代码为:
[html]   view plain  copy  在CODE上查看代码片  派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <unistd.h>  
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5.    
  6. void show_ids (void)  
  7. {  
  8.         printf ("The real user ID is: %d\n", getuid());  
  9.         printf ("The effective user ID is :%d\n", geteuid());  
  10. }  
  11.    
  12. int main(void)  
  13. {  
  14.         const char *file = "/root/rootfile3.txt";  
  15.    
  16.         show_ids();  
  17.         if (!unlink (file)) {  
  18.                 printf ("Ok, I am root, and I can delete the file which in /root directory.\n");  
  19.                 system ("echo hello world > /root/rootfile3.txt");  
  20.                 printf ("Now, drop the root privileges.\n");  
  21.                 if (seteuid (1000) < 0) {  
  22.                         perror ("setuid");  
  23.                         exit (EXIT_FAILURE);  
  24.                 }  
  25.                 show_ids();  
  26.                 if (unlink (file) < 0) {  
  27.                         printf ("Ok, we have no privilege to delete rootfile3.txt.\n");  
  28.                 }  
  29.                 printf ("try to regain root power again...\n");  
  30.                 if (seteuid (0)) {  
  31.                         perror ("seteuid");  
  32.                         show_ids();  
  33.                         exit (EXIT_FAILURE);  
  34.                 }  
  35.         }  
  36.                 show_ids();  
  37.    
  38.                 printf ("try to delete rootfile3.txt again\n");  
  39.                 if (!unlink(file)) {  
  40.                         printf ("Ok, regain root power successful!\n");  
  41.                         system ("echo hello world > /root/rootfile3.txt");  
  42.                         return (0);  
  43.                 }  
  44.    
  45.         return (0);  
  46. }  
在上面的代码中,我们将原来的 setuid(1000) 替换为 seteuid(1000); 。并且在此后,再次尝试删除 /root/rootfile3.txt 这个文件。下面在普通用户下运行该程序:
beyes@debian:~/C/syscall/getuid$ ./getuid3 
The real user ID is: 1000 
The effective user ID is :0 
Ok, I am root, and I can delete the file which in /root directory. 
Now, drop the root privileges. 
The real user ID is: 1000 
The effective user ID is :1000 
Ok, we have no privilege to delete rootfile3.txt. 
try to regain root power again... 
The real user ID is: 1000 
The effective user ID is :0 
try to delete rootfile.txt again 
Ok, regain root power successful!
此时我们看到整个过程:
先是普通用户执行了具有 root 有效权限位设置的程序,它成功的删除了 /root 下面的一个文本文件;然后使用 system() 系统调用恢复了该文件,目的是方便下面继续实验。接着,它使用 seteuid() 函数时该进程降权为普通用户权限进程。此后,正是因为有了 saved set-user-ID 的保存,所以当再次使用 seteuid() 恢复 进程的 root 权限时可以恢复成功!

所以再次看到,setuid() 会改变 saved set-user-ID 的值而不能恢复权限;而 seteuid() 不会改变 saved set-user-ID 这个值,所以它能够恢复。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值