Linux RealUID、保存的 UID、有效的 UID。这是怎么回事?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8499296/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 03:38:32  来源:igfitidea点击:

RealUID, Saved UID, Effective UID. What's going on?

linuxprivilegessetuid

提问by Lelouch Lamperouge

This is a set-root-uid program

这是一个 set-root-uid 程序

$ls -l
-rwsr-sr-x 1 root root 7406 2011-12-13 22:37 ./x*

The source code:

源代码:

int main(void) {
    printf(
        "         UID           GID  \n"
        "Real      %d  Real      %d  \n"
        "Effective %d  Effective %d  \n",
             getuid (),     getgid (),
             geteuid(),     getegid()
    );

seteuid(600);
    printf(
        "         UID           GID  \n"
        "Real      %d  Real      %d  \n"
        "Effective %d  Effective %d  \n",
             getuid (),     getgid (),
             geteuid(),     getegid()
    );

setuid(1000);

    printf(
        "         UID           GID  \n"
        "Real      %d  Real      %d  \n"
        "Effective %d  Effective %d  \n",
             getuid (),     getgid (),
             geteuid(),     getegid()
    );

setuid(0); // HOW DOES THIS SUCCEED IN SETTING THE EUID BACK TO 0
    printf(
        "         UID           GID  \n"
        "Real      %d  Real      %d  \n"
        "Effective %d  Effective %d  \n",
             getuid (),     getgid (),
             geteuid(),     getegid()
    );

    return 0 ;       
}

OUTPUT

输出

         UID           GID  
Real      1000  Real      1000  
Effective 0  Effective 0  
         UID           GID  
Real      1000  Real      1000  
Effective 600  Effective 0  
         UID           GID  
Real      1000  Real      1000  
Effective 1000  Effective 1000  
         UID           GID  
Real      1000  Real      1000  
Effective 0  Effective 1000  

My question

我的问题

The man page states that setuid will change the real,saved and effective uid. So after the calling setuid(1000), all three change to 1000. How is that setuid(0)let's me change euidto 0?

手册页指出 setuid 将更改真实的、保存的和有效的 uid。所以在调用之后setuid(1000),三个都变成了1000。这是怎么回事setuid(0)咱们我的变化euid0

采纳答案by Ajai

There are two cases,

有两种情况,

  1. You want to temporarily drop root privilege while executing setuid program
  2. You want to permanently drop root privilege while executing setuid program...
  1. 您想在执行 setuid 程序时暂时放弃 root 权限
  2. 您想在执行 setuid 程序时永久删除 root 权限...
  • You can temporarily do it by setting the euid to the real user id and then changing the uid to anything you want.And later when you need the root privilege back you can setuid to root and the effective userid will change back to root. This is because the saved user id is not changed.
  • You can drop privilege permanently by changing the uid straight away to a lesser privileged user id. After this no matter what you cannot get back the root privilege.
  • 您可以通过将 euid 设置为真实用户 ID 然后将 uid 更改为您想要的任何内容来临时完成此操作。稍后当您需要恢复 root 权限时,您可以将 uid 设置为 root,有效用户 ID 将更改回 root。这是因为保存的用户 ID 没有改变。
  • 您可以通过立即将 uid 更改为较低权限的用户 ID 来永久删除权限。在此之后,无论如何您都无法取回 root 权限。

Case 1:

情况1:

After a setuid program starts executing

setuid 程序开始执行后

1.seteuid(600);
2.setuid(1000);
3.setuid(0);

For this case the root privilege can be gained back again.

对于这种情况,可以再次获得 root 权限。

              +----+------+------------+
              | uid|euid  |saved-uid   |
              |----|------|------------|
            1.|1000| 0    | 0          |
            2.|1000| 600  | 0          |
            3.|1000| 1000 | 0          |
            4.|1000|  0   | 0          |
              |    |      |            |
              +------------------------+

Case 2:

案例2:

After a setuid program starts executing,

在 setuid 程序开始执行后

1.setuid(1000);
2.setuid(0);



               +----+------+------------+
               | uid|euid  |saved-uid   |
               |----|------|------------|
             1.|1000|0     | 0          |
             2.|1000|1000  | 1000       |
               |    |      |            |
               +------------------------+

In this case you cannot get back the root privilege. This can be verified by the following command,

在这种情况下,您无法取回 root 权限。这可以通过以下命令验证,

cat /proc/PROCID/task/PROCID/status | less

cat /proc/PROCID/task/PROCID/status | 较少的

Uid:    1000    0       0       0
Gid:    1000    0       0       0

This command will display a Uid and Gid and it will have 4 fields( the first three fields are the one we are concerned with). Something like the above

这个命令将显示一个 Uid 和 Gid,它将有 4 个字段(前三个字段是我们关注的字段)。类似上面的东西

The three fields represent uid,euid and saved-user-id. You can introduce a pause (an input from user) in your setuid program and check for each step the cat /proc/PROCID/task/PROCID/status | lesscommand. During each step you can check the saved uid getting changed as mentioned.

这三个字段分别代表uid、euid 和saved-user-id。您可以在 setuid 程序中引入暂停(来自用户的输入)并检查cat /proc/PROCID/task/PROCID/status | less命令的每一步。在每个步骤中,您都可以检查保存的 uid 是否如上所述发生更改。

If you're euid is root and you change the uid, the privileges gets dropped permanently.If effective user id is not root then saved user id is never touched and you can regain the root privilege back anytime you want in your program.

如果您的 euid 是 root 并且您更改了 uid,则权限将永久删除。如果有效用户 ID 不是 root,则永远不会触及保存的用户 ID,您可以随时在程序中重新获得 root 权限。

回答by BRPocock

DESCRIPTIONsetuid() sets the effective user ID of the calling process. If the effective UID of the caller is root, the real UID and saved set-user-ID are also set.

Under Linux, setuid() is implemented like the POSIX version with the _POSIX_SAVED_IDS feature. This allows a set-user-ID (other than root) program to drop all of its user privileges, do some un-privileged work, and then reengage the original effective user ID in a secure manner.

If the user is root or the program is set-user-ID-root, special care must be taken. The setuid() function checks the effective user ID of the caller and if it is the superuser, all process-related user ID's are set to uid. After this has occurred, it is impossible for the program to regain root privileges.

Thus, a set-user-ID-root program wishing to temporarily drop root privileges, assume the identity of an unprivileged user, and then regain root privileges afterward cannot use setuid(). You can accomplish this with seteuid(2).

说明setuid() 设置调用进程的有效用户 ID。如果调用者的有效 UID 为 root,则同时设置真实 UID 和保存的 set-user-ID。

在 Linux 下,setuid() 的实现方式与 POSIX 版本类似,具有 _POSIX_SAVED_IDS 功能。这允许 set-user-ID(root 除外)程序放弃其所有用户权限,执行一些非特权工作,然后以安全的方式重新使用原始有效用户 ID。

如果用户是 root 或程序是 set-user-ID-root,则必须特别小心。setuid() 函数检查调用者的有效用户 ID,如果它是超级用户,则所有与进程相关的用户 ID 都设置为 uid。发生这种情况后,程序将无法重新获得 root 权限。

因此,一个 set-user-ID-root 程序希望暂时放弃 root 权限,假设一个非特权用户的身份,然后重新获得 root 权限,就不能使用 setuid()。您可以使用 seteuid(2) 完成此操作。

(from the Linux Programmers' Manual, 2014-09-21, page setuid.2)

(来自 Linux Programmers' Manual, 2014-09-21, page setuid.2

回答by pilcrow

O! These functions are difficult to use correctly.

哦!这些功能很难正确使用。

The man page states that setuid will change the real,saved and effective uid. So after the calling setuid(1000), all three change to 1000.

手册页指出 setuid 将更改真实的、保存的和有效的 uid。所以在调用 setuid(1000) 之后,三个都变成了 1000。

That is the case if and only if you are euid 0. At the time you call setuid(0), however, you are euid 1000 and saveduid 0 (check getresuid(2), for example). That's why you're able to regain privileges.

当且仅当您是 euid 0 时才会出现这种情况setuid(0)。但是,在您调用时,您是 euid 1000 并保存了uid 0(getresuid(2)例如,检查)。这就是您能够重新获得特权的原因。

回答by J.Wu

Code:

代码:

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>

void print_uid(char *str, int ret)
{
    uid_t ruid;
    uid_t euid;
    uid_t suid;
    getresuid(&ruid, &euid, &suid);

    printf("%s ret:%d\n"
           "Real:%4d  Effective:%4d  Saved:%4d\n",
           str, ret, ruid, euid, suid);
}

int main(void)
{
    int ret = 0;
    print_uid("init", ret);            /* Real:1000  Effective:   0  Saved:   0 */

    ret = seteuid(600);
    print_uid("seteuid(600)", ret);    /* Real:1000  Effective: 600  Saved:   0 */

    ret = setuid(1000);
    print_uid("setuid(1000)", ret);    /* Real:1000  Effective:1000  Saved:   0 */

    ret = setuid(0);
    print_uid("setuid(0)", ret);       /* Real:1000  Effective:   0  Saved:   0 */

    ret = setuid(1000);
    print_uid("setuid(1000)", ret);    /* Real:1000  Effective:1000  Saved:1000 */

    ret = setuid(0);
    print_uid("setuid(0)", ret);       /* Real:1000  Effective:1000  Saved:1000 */

    return 0 ;       
}

sudo chown root setuid_feature
sudo chmod +s setuid_feature

须藤 chown 根 setuid_feature
须藤 chmod +s setuid_feature

There are three uids for a process in Linux: REAL uid, EFFECTIVE uid, SAVED uid.
Cond 1. When euid is root, setuid or seteuid can be set to any uid, but there is a side effect, when using setuid(not seteuid), all of the three can be set to the same uid which is not ROOT, and then the process can't regain ROOT privilege.
Cond 2. When euid is not root, setuid or seteuid can be set to ruid or suid, and only euid is changed.

Linux 中的进程有 3 个 uid:REAL uid、EFFECTIVE uid、SAVED uid。
Cond 1. 当euid为root时,setuid或seteuid可以设置为任意的uid,但是有一个副作用,当使用setuid(not seteuid)时,三个都可以设置为同一个非ROOT的uid,并且那么该进程就无法重新获得 ROOT 权限。
Cond 2. euid不是root时,setuid或seteuid可以设置为ruid或suid,只改变euid。

                       |      seteuid             |          setuid  
Cond 1. (euid == root) | set euid to any uid      | set all three uids to any uid  
Cond 2. (euid != root) | set euid to ruid or suid | set euid to ruid or suid  

so, there are 5 setuid or seteuid process there in code, let me classify them:
1. seteuid(600): Cond 1, set euid to 600
2. setuid(1000): Cond 2, set euid to 1000
3. setuid(0) : Cond 2, set euid to 0(suid)
4. setuid(1000): Cond 1, set all three uids to 1000
5. setuid(0) : Cond 2, all three uids is not equal 0, so can't set to 0, failed with ret = -1

所以,代码中有5个setuid或seteuid进程,让我对它们进行分类:
1. seteuid(600): Cond 1, set euid to 600
2. setuid(1000): Cond 2, set euid to 1000
3. setuid( 0) : Cond 2, 将 euid 设置为 0(suid)
4. setuid(1000): Cond 1, 将所有三个 uid 设置为 1000
5. setuid(0) : Cond 2, 所有三个 uid 不等于 0, 所以可以' t 设置为 0,失败,ret = -1