转载请注明来源chengyaogen.blog.chinaunix.net

一、进程与权限

A.进程时Linux/Unix操作系统中最重要的抽象之一

B.进程是一个处于执行期的程序(目标代码存储在某种介质上)
 A    process   is   a  program(object  code  stored  on some  media)  in the  midst  of  
execution.

而进程在执行过程中经常涉及到文件访问操作等
1.一个运行中的进程究竟可以访问哪些资源,而不能访问哪些资源?
2.一个进程运行过程中如何实现在某个执行阶段拥有一些权限而在另一个阶段又具备另外一种权限呢?

上述状况对应于进程的访问权限以及进程执行中不同权限的切换。这一切都和进程实际用户ID,进程有效用户ID,进程保存设置用户ID有关联。

二、进程的用户ID

1.实际用户ID(real user id,RUID)

为该进程的创建者的用户ID,也可以说是进程的执行者。该ID仅root用户可以修改

2.有效用户ID(effective  user  id,EUID)

该ID用户标识用户进程执行操作的权限。例如:如果EUID是0即(root),此时进程拥有root用户权限。普通用户可以将EUID设置为RUID或者SUID,而超级用户可以将EUID设置为任意的合法UID。

 注意:对于一个可执行的二进制文件,如果其set-user-id bit被设置,则运行改程序时,对应进程的有效用户ID(EUID)为该文件的文件所有者用户ID。

3.保存设置用户ID(saved set-user-id,SUID)

对于没有设置set-uid-bit的可执行程序而言,其对应进程的保存设置用户ID(SUID)为其实际用户ID;而对于设置了set-uid-bit的程序而言,其对应的保存设置用户ID(SUID)为该可执行文件的文件拥有者用户ID。只能root用户权限,才能更改(当一个程序文件运行的时候,其值已经确定)。

注意:linux不提供返回保存的设置-用户-ID的函数

总结:
RUID代表此进程是哪个用户创建的, EUID代表此进程所拥有的权限,SUID保存了EUID,当EUID改变后,如果想回到以前的EUID,此时SUID将发挥作用。

三、相关API

#include
#include

int   setuid(uid);

可以用setuid函数设置实际用户ID和有效用户ID。注意,我们并不能想怎么设就怎么设。有若干规则需要我们遵守:

(1)若进程具有超级用户特权,则setuid函数将实际用户ID、有效用户ID,以及保存的设置-用户-D设置为uid。

(2)若进程没有超级用户特权,但是uid等于实际用户ID或保存的设置-用户-ID,则setuid只将有效用户ID设置为。不改变实际用户ID和保存的设置-用户-ID。

(3)如果上面两个条件都不满足,则errno设置为EPERM,并返回出错.。

关于内核所维护的三个用户ID,还要注意下列几点

(1)只有超级用户进程可以更改实际用户ID。通常,实际用户ID是在用户登录时,由login(1)程序设置的,而且决不会改变它。因为login是一个超级用户进程,当它调用setuid时,设置所有三个用户ID。

(2)仅当对程序文件设置了 set-user-id bit时,exec函数设置有效用户ID为文件所有者。如果set-user-id bit没有设置,则exec函数不会改变有效用户ID,而将其位置原先值。任何时候都可以调用setuid,将有效用户ID设置为实际用户ID或保存的设置-用户-ID。自然,不能讲有效用户ID设置为任意随机值。

四、探究

案例一:
有个要在MacOS上实现一个关机程序,它熟悉linux,其实老祖宗都是unix,所以MacOS对他并不陌生。如下他写了如下程序实现关机

一步步探究linux进程中的用户ID-LMLPHP

编译运行:

一步步探究linux进程中的用户ID-LMLPHP

从上面看,这段代码可以实现关机,但是有个不符合要求的地方,运行程序不能是超级用户,只能是普通用户。

他很苦恼,后来找到了我,我做了如下事情

一步步探究linux进程中的用户ID-LMLPHP

呵呵,可以了。我将这个可执行文件的owner和group都改为了root,然后有讲其set-user-id bit位加以了设置。这样以普通用户运行这个程序时,此进程的EUID就是root了,这样满足了shutdown命令执行的权限。

案例二:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>

  7. void test_read_file(const char *name)
  8. {
  9.     int fd = -1;

  10.     fd = open(name ,O_RDWR);
  11.     if(fd < 0){
  12.         printf("=[ERROR]:read failed.\n");
  13.     }else{
  14.         printf("=[OK]:read successful\n");
  15.         close(fd);
  16.     }
  17. }

  18. //打印uid和euid
  19. void p_states()
  20. {
  21.     int uid = 0;
  22.     int euid = 0;

  23.     printf("------Current states--------\n");
  24.     printf("real uid\t %d\n",getuid());
  25.     printf("effective uid\t %d\n",geteuid());
  26.     printf("----------------------------\n");
  27. }

  28. //调用setuid
  29. void run_setuid_fun(int uid)
  30. {
  31.     if(setuid(uid) == -1)
  32.     {
  33.         printf("=[ERROR]:setuid(%d) error\n",uid);
  34.     }

  35.     p_states();
  36. }

  37. //调用setuid
  38. void run_seteuid_fun(int uid)
  39. {
  40.     if(setuid(uid) == -1)
  41.     {
  42.         printf("=[ERROR]:seteuid(%d) error\n",uid);
  43.     }

  44.     p_states();
  45. }

  46. int main()
  47. {
  48.     int t_re = 0;
  49.     const char *file = "root_only.txt";

  50.     printf("\nTEST 1:\n");
  51.     p_states();
  52.     //此时real uid = login user id
  53.     //effective uid = root
  54.     //saved uid = root
  55.     test_read_file(file);
  56.     
  57.     getchar();

  58.     printf("\nTEST 2:setuid(getuid())\n");
  59.     run_seteuid_fun(getuid());
  60.     //此时real uid = login user id
  61.     //effective uid = login user id
  62.     //saved uid = root
  63.     test_read_file(file);
  64.     
  65.     getchar();

  66.     printf("\nTEST 3:setuid(0)\n");
  67.     run_setuid_fun(0);
  68.     //此时real uid = login user id
  69.     //effective uid = root
  70.     //saved uid = root
  71.     test_read_file(file);

  72.     getchar();

  73.     printf("\nTEST 4:setuid(0)\n");
  74.     run_setuid_fun(0);
  75.     //此时real uid = root
  76.     //effective uid = root
  77.     //saved uid = root
  78.     test_read_file(file);
  79.     
  80.     getchar();

  81.     printf("\nTEST 5:setuid(503)\n");
  82.     run_setuid_fun(503);
  83.     //此时real uid = login user id
  84.     //effective id = login user id
  85.     //saved uid = login user id
  86.     test_read_file(file);

  87.     getchar();

  88.     printf("\nTEST 6:setuid(0)\n");
  89.     //read uid = login user id
  90.     //effective uid = login user id
  91.     //saved uid = login user id
  92.     run_setuid_fun(0);
  93.     test_read_file(file);

  94.     return 0;
  95. }
编译运行:

一步步探究linux进程中的用户ID-LMLPHP

root_only.txt文件建立:

一步步探究linux进程中的用户ID-LMLPHP

第一次:

RUID:实际用户
EUID:文件所有者(root)
SUID:文件所有者(root)

一步步探究linux进程中的用户ID-LMLPHP

此时进程拥有root用户权限,能对root_only.txt进行读写操作

第二次:

此时普通用户调用setuid(getuid()),只会将EUID改为getuid(),其他都不变
RUID:实际用户
EUID:实际用户
SUID:root

一步步探究linux进程中的用户ID-LMLPHP

此时进程没有有root用户权限,不能能对root_only.txt进行读写操作

第三次:

此时普通用户调用seteuid(0),只会将EUID改为0,其他都不变
RUID:实际用户
EUID:root
SUID:root

一步步探究linux进程中的用户ID-LMLPHP

此时进程拥有root用户权限,能对root_only.txt进行读写操作

第四次:

此时进程拥有root用户权限,调用setuid(0),会将三个ID都设置为0
RUID:root
EUID:root
SUID:root

一步步探究linux进程中的用户ID-LMLPHP

此时进程拥有root用户权限,能对root_only.txt进行读写操作

第五次:

此时进程拥有root用户权限,调用setuid(503),会将三个ID都设置为503
RUID:503
EUID:503
SUID:503

一步步探究linux进程中的用户ID-LMLPHP

此时进程拥有普通用户权限,不能对root_only.txt进行读写操作

第六次:

此时进程拥有普通用户权限,调用setuid(0),此时RUID,SUID都不为0,这一次操作将失败
RUID:503
EUID:503
SUID:503

一步步探究linux进程中的用户ID-LMLPHP

此时进程拥有普通用户权限,不能能对root_only.txt进行读写操作
12-28 07:34