【问题标题】:Switch user without creating an intermediate process切换用户而不创建中间进程
【发布时间】:2018-04-27 07:10:37
【问题描述】:

我可以使用sudosu 作为另一个用户执行命令。通过结合exec,我可以将当​​前进程替换为sudosu,以及一个运行该命令的子进程。但我想用作为另一个用户运行的命令替换当前进程。我该怎么做?

sleep inf 作为命令,someguy 作为用户进行测试:

exec su someguy -c 'sleep inf'

这给了我来自pstree

bash───su───sleep

exec sudo -u someguy sleep inf

给予

bash───sudo───sleep

在这两种情况下,我只需要 sleep 命令,以 bash 作为父级。

我希望我可以通过 setuid()exec() 的一些序列从 C 中做到这一点。

【问题讨论】:

  • 目前 bash 被 susudo 替换,它运行一个子 sleep。您希望 bashsleep 作为 bash 的子级吗?
  • @TarunLalwani 我要 bash--sleep。
  • 看看我发布的答案是否有帮助。在 Ubuntu 16.04 上测试

标签: linux bash shell exec sudo


【解决方案1】:

sudo sleepexec sudo sleep的区别在于第二条命令中sudo进程替换了bash镜像,sleep退出时调用shell进程退出

pstree -p $$
bash(8765)───pstree(8943)

((sleep 1; pstree -p $$ )&); sudo -u user sleep 2
bash(8765)───sudo(8897)───sleep(8899)

((sleep 1; pstree -p $$ )&); exec sudo -u user sleep 2
sudo(8765)───sleep(8993)

然而,sudosu 派生新流程这一事实取决于设计及其实现(一些来源可找到 here)。

来自 sudo 手册页:

流程模型

sudo 运行命令时,会调用 fork(2),如上所述设置执行环境,并在子进程中调用 execve 系统调用。主要须藤 进程等待命令完成,然后将命令的退出状态传递给安全策略的关闭函数并退出。如果 I/O 日志记录插件是 config- 如果安全策略明确要求,则创建一个新的伪终端(“pty”),并使用第二个 sudo 进程在 用户现有的 pty 和正在运行命令的新 pty。这个额外的过程使得可以暂停和恢复命令。没有它,com- mand 将是 POSIX 术语中的“孤立进程组”,它不会收到任何作业控制信号。作为一种特殊情况,如果策略插件没有定义 close 函数并且不需要 pty,sudo 将直接执行命令,而不是先调用 fork(2)。 sudoers 策略插件只会定义一个关闭函数 启用 I/O 日志记录时,需要 pty,或者启用 pam_session 或 pam_setcred 选项。请注意 pam_session 和 pam_setcred 在系统上默认启用 使用 PAM 的项目。

【讨论】:

  • 似乎 sudo 代码中的某些内容阻止了它直接执行。如果您知道如何确保这种行为,将不胜感激。
【解决方案2】:

我不分享观察和结论。见下文:

我创建了两个 shellscript:

$ cat just_sudo.sh
#!/bin/bash
sudo sleep inf
$ cat exec_sudo.sh
#!/bin/bash
exec sudo sleep inf

所以,一个有执行官,一个没有。如果我做一个pstree 来查看起始情况,我会得到:

$ pstree $$
bash───pstree
$ echo $$
17250

这给了我基线。接下来我启动了两个脚本:

$ bash just_sudo.sh &
[1] 1218
$ bash exec_sudo.sh &
[2] 1220

然后,pstree 给出:

$ pstree $$
bash─┬─bash───sleep
     ├─pstree
     └─sleep

第一个是just_sudo,第二个是exec_sudo。两者都以 root 身份运行:

$ ps -ef | grep sleep
root      1219  1218  0 14:01 pts/4    00:00:00 sleep inf
root      1220 17250  0 14:01 pts/4    00:00:00 sleep inf

第一个是just_sudo,第二个是exec_sudo。可以看到,exec_sudo 中 sleep 的 parent-PID 是启动脚本的交互式 shell,PID 是 1220,也就是我们在后台启动脚本时看到的 PID。

如果您使用两个终端窗口并且不将其置于后台,这也可以:

terminal 1                            terminal 2
$ echo $$
16053                                 $ pstree 16053
                                      bash
$ sudo sleep inf
                                      $ pstree 16053
                                      bash───sleep
^C
$ exec sudo sleep inf
                                      $ pstree 16053
                                      sleep
^C
 ( window is closed )

所以,在 my linux 系统上,行为并不像你建议的那样。sudo 可能保留在进程树中的唯一方法是它在现有的 tty 中运行(所以没有exec),或者使用伪终端调用它,例如exec sudoedit

【讨论】:

  • 您的回答没有得到我的支持。您的观察和结论与问题作者不同的原因是您在后台运行 *_sudo.sh 脚本(在命令行末尾带有 & 字符)。它的解释可以在源代码中找到 - 请参阅sudo.ws/repos/sudo/file/tip/src/exec.c 并注意标有注释的代码片段,其中包含直接执行)。使用提供的信息改进您的答案,您将收到我的支持。
  • 后台运行不是原因,正如在不同终端中运行所证明的那样。该代码指出,只有在存在 I/O 插件或策略已请求 pty 时,才会执行直接执行。通常只有调用sudoedit 时才会出现这种情况。否则你必须做一些非常特别的事情。
  • 我不明白你用 exec_sudo.sh 描述的行为。
  • @LjmDullaart 你的操作系统是什么?我认为这是高度定义的实现。
  • 都是Linux。 Fedora 23、Slackware 14.2(均为 64 位)和 Raspian Jessie。
【解决方案3】:

为了释放一个命令,你必须给他std io

为此,您可以关闭所有 stdinstdoutstderr 或让它们指向别处。

试试这个:

su - someguy -c 'exec nohup sleep 60 >/tmp/sleep.log 2>/tmp/sleep.err <<<"" &'

注意:

su - someguy -c 'exec nohup sleep 60 &'

够了,而且

su - someguy -c 'exec sleep 60 >/tmp/sleep.log 2>/tmp/sleep.err <<<"" &'

也可以。

考虑看看man nohup

注意 2:在 下,您可以使用:

su - someguy -c 'exec sleep 60 & disown -h'

...然后阅读help disownman bash

展示如何关闭所有 IO 的小演示:

su - someguy -c 'exec 0<&- ; exec 1>&- ; exec 2>&- ; exec sleep 60 &'

快速测试:

pstree $(ps -C sleep ho pid)
sleep

【讨论】:

  • 这不好,因为你正在分离睡眠进程,并且它被重新设置为 init。我需要 sleep 才能成为最初调用 su 或 sudo 的 bash 进程的子进程。
【解决方案4】:

我不确定这是否可以使用sudosu 来完成。但是您可以使用简单的c 程序轻松实现此目的。我将展示一个带有硬编码命令和用户 ID 的极简版,但您始终可以根据自己的喜好对其进行自定义

test.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>

 int runAs(int gid, int uid, char *command[]) {
    setgid(gid);
    setuid(uid);
    char *args[]={"sleep","inf",NULL};
    execvp(args[0],args);
 }


 int main(int argc, char *argv[] )
 {
     runAs(1000, 65534, argv);
     return 0;
 }

注意:在我的机器上1000vagrant 用户和组的uid/gid。 65534nobody用户和组的uid和gid

build.sh

#!/bin/bash

sudo gcc test.c -o sosu
sudo chown root:root sosu
sudo chmod u+s sosu

现在是测试时间

$ pstree $$ -p
bash(23251)───pstree(28627)

$ ./sosu

现在从另一个终端

$ pstree -p 23251
bash(23251)───sleep(28687)

$ ps aux | grep [2]8687
nobody   28687  0.0  0.0   7288   700 pts/0    S+   11:40   0:00 sleep inf

如您所见,该进程以nobody 运行,它是bash 的子进程

【讨论】:

  • 我认为这最终可能是必要的。
猜你喜欢
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 2014-09-16
  • 2023-03-30
  • 2012-05-29
  • 1970-01-01
  • 2016-04-04
  • 2016-02-18
相关资源
最近更新 更多