切换用户而不创建中间过程

时间:2017-11-14 11:01:58

标签: linux bash shell exec sudo

我可以使用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中执行此操作。

4 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:5)

我不同意观察和结论。见下文:

我创建了两个shellcripts:

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

所以,一个有一个exec,一个没有。如果我做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中睡眠的父PID是从中启动脚本的交互式shell,PID是1220,这是我们在后台启动脚本时看到的PID。 / p>

如果您使用两个终端窗口而不将它放在后台,这也可以:

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 )

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

答案 2 :(得分:2)

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

为此,您可以关闭所有 stdin stdout stderr ,或让它们指向其他位置。

试试这个:

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

答案 3 :(得分:2)

我不确定是否可以使用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