您如何通过多个sudo和su命令找到原始用户?


93

通过sudo或su运行脚本时,我想获得原始用户。无论发生多次sudo还是su相互内部运行,都应该发生sudo su -

Answers:


136

结果:

使用who am i | awk '{print $1}'OR,logname因为无法保证没有其他方法。

以自身身份登录:

evan> echo $USER
evan
evan> echo $SUDO_USER

evan> echo $LOGNAME
evan
evan> whoami
evan
evan> who am i | awk '{print $1}'
evan
evan> logname
evan
evan>

普通sudo:

evan> sudo -s
root> echo $USER
root
root> echo $SUDO_USER
evan
root> echo $LOGNAME
root
root> whoami
root
root> who am i | awk '{print $1}'
evan
root> logname
evan
root>

须藤su-:

evan> sudo su -
[root ]# echo $USER
root
[root ]# echo $SUDO_USER

[root ]# echo $LOGNAME
root
[root ]# whoami
root
[root ]# who am i | awk '{print $1}'
evan
[root ]# logname
evan
[root ]#

苏多苏-; su tom:

evan> sudo su -
[root ]# su tom
tom$ echo $USER
tom
tom$ echo $SUDO_USER

tom$ echo $LOGNAME
tom
tom$ whoami
tom
tom$ who am i | awk '{print $1}'
evan
tom$ logname
evan
tom$

1
在这种情况下,您可以使用who | awk '{print $1}'
SiegeX'1

2
...如果您是唯一登录的人(并且只有一次)。
暂停,直到另行通知。

9
您只需要2个参数:who am i与相同who smells bad。此外,它仅在STDIN与TTY关联时才起作用。因此,如果您运行echo "hello" | who am i它,将根本无法工作。
tylerl 2011年

1
echo "hello" | who am i除非脚本在没有终端的环境中运行,否则您将无法正常运行。然后,您可能会看到由于who am i无法读取的stdin出现某种问题而导致的错误不起作用,在这种情况下,您可能会who am i出于绝望的目的而尝试将数据管道传输到绝望状态。tylerl只是指出他已经走了那条路,而且管道无法正常工作,因为stdin必须可读且与TTY相关联。
Edwin Buck

4
@even是的,尽管我希望它需要尽可能少的配置,所以我现在正在使用logname,事实证明它可以工作,而在哪里who am i却不行。
Bart van Heukelom

18

没有完美的答案。更改用户ID时,通常不会保留原始用户ID,因此信息会丢失。某些程序(例如lognamewho -m实施hack),他们在其中检查查看连接到哪个终端stdin,然后检查以查看哪个用户在该终端上登录。

该解决方案通常可以使用,但并非万无一失,当然也不应该认为它是安全的。例如,假设是否who输出以下内容:

tom     pts/0        2011-07-03 19:18 (1.2.3.4)
joe     pts/1        2011-07-03 19:10 (5.6.7.8)

tom用来su扎根,并运行您的程序。如果STDIN未重定向,logname则将输出类似的程序tom。如果是这样重定向的(例如从文件):

logname < /some/file

no login name由于输入不是终端,因此结果为“ ”。但是,更有趣的是,用户可能会冒充其他登录用户的事实。由于Joe在pts / 1上登录,Tom可以假装通过运行成为他

logname < /dev/pts1

现在,它说joe即使tom是执行命令的人。换句话说,如果您在任何类型的安全角色中使用此机制,那么您就疯了。


2
如果您自己运行脚本(如所使用的命令所示),安全性不是问题。如果是这样,那么您还有很多问题,因为他们也拥有sudo访问权限。该人员可以复制脚本并以自己喜欢的任何方式对其进行修改。这只是获取登录名称以在脚本中使用的一种方法。还是我想念您在说什么?
evan

1
@evan:具有sudo访问权限并不意味着可以覆盖文件。
Flimzy

@Flimzy在什么情况下root不能覆盖文件?
伊万

1
@evan:很明显,任何时候您的sudo访问都不能使您访问shell或任何其他可以覆盖文件的命令。
Flimzy

@evan sudo访问不总是总root访问权限(在大多数管理员情况下不应该这样)。这是一组可配置的受限执行上下文。
DylanYoung

8

这是ksh我在HP-UX上编写的功能。我不知道它将如何Bash在Linux中使用。这个想法是该sudo进程以原始用户身份运行,而子进程是目标用户。通过循环回父流程,我们可以找到原始流程的用户。

#
# The options of ps require UNIX_STD=2003.  I am setting it
# in a subshell to avoid having it pollute the parent's namespace.
#
function findUser
{
    thisPID=$$
    origUser=$(whoami)
    thisUser=$origUser
    while [ "$thisUser" = "$origUser" ]
    do
        ( export UNIX_STD=2003; ps -p$thisPID -ouser,ppid,pid,comm ) | grep $thisPID | read thisUser myPPid myPid myComm
        thisPID=$myPPid
    done
    if [ "$thisUser" = "root" ]
    then
        thisUser=$origUser
    fi
    if [ "$#" -gt "0" ]
    then
        echo $origUser--$thisUser--$myComm
    else
        echo $thisUser
    fi
    return 0
}

我知道最初的问题来自很久以前,但是人们(例如我)仍然在问,这似乎是提出解决方案的好地方。


5

如何使用logname(1)获取用户的登录名?


logname(1)不起作用,但logname可以-将结果添加到上方
-evan

最初我曾尝试过,$LOGNAME但是没有用。也添加到上面的结果中。
evan

是否logname仍然需要一个tty?通过我的测试,它始终会通过。(也许我出了点问题。)我正在使用coreutils 8.26运行linux。
simohe

我的登录名(GNU coreutils)8.28始终返回“登录名:无登录名”(Ubuntu 18.04.2)
sondra.kinsey,


2

user1683793的findUser()函数已移植bash并扩展,因此它也返回存储在NSS库中的用户名。

#!/bin/bash

function findUser() {
    thisPID=$$
    origUser=$(whoami)
    thisUser=$origUser

    while [ "$thisUser" = "$origUser" ]
    do
        ARR=($(ps h -p$thisPID -ouser,ppid;))
        thisUser="${ARR[0]}"
        myPPid="${ARR[1]}"
        thisPID=$myPPid
    done

    getent passwd "$thisUser" | cut -d: -f1
}

user=$(findUser)
echo "logged in: $user"

仅供参考:此功能(及其基于的功能)将不会循环通过sudo相互嵌套产生的多个外壳。
asdfghjkl 2013年

2

循环返回并提供用户列表

根据user1683793的回答

通过排除非TTY进程,我跳过root作为登录的发起者。我不确定在某些情况下这是否可能导致过多损失

#!/bin/ksh
function findUserList
{
    typeset userList prevUser thisPID thisUser myPPid myPid myTTY myComm
    thisPID=$$                 # starting with this process-ID
    while [ "$thisPID" != 1 ]  # and cycling back to the origin
    do
        (  ps -p$thisPID -ouser,ppid,pid,tty,comm ) | grep $thisPID | read thisUser myPPid myPid myTTY myComm
        thisPID=$myPPid
        [[ $myComm =~ ^su ]] && continue        # su is always run by root -> skip it
        [[ $myTTY == '?' ]] && continue         # skip what is running somewhere in the background (without a terminal)
        if [[ $prevUser != $thisUser ]]; then   # we only want the change of user
                prevUser="$thisUser"            # keep the user for comparing
                userList="${userList:+$userList }$thisUser"  # and add the new user to the list
        fi
        #print "$thisPID=$thisUser: $userList -> $thisUser -> $myComm " >&2
    done
    print "$userList"
    return 0
}

logname或者who am i没有给我想要的答案,尤其不能在较长的列表su user1su user2su user3...

我知道最初的问题来自很久以前,但是人们(例如我)仍然在问,这似乎是提出解决方案的好地方。


2

多次调用ps的替代方法:执行一次pstree调用

pstree -lu -s $$ | grep --max-count=1 -o '([^)]*)' | head -n 1

输出(登录为偶数时): (evan)

pstree参数:

  • -l:长行(不缩短)
  • -u:显示用户何时更改为(userName)
  • -s $$:显示此过程的父项

获取第一用户的变化(这是登录)与grep -ohead

限制:该命令可能不包含任何大括号()(通常不正常)


pstree -lu -s $$ | head -n1 | sed -e's / [^(] *(([^)] *))。* / \ 1 /'
Alexx Roche

0

在运行systemd-logind的系统上,systemd API提供了此信息。如果要从Shell脚本访问此信息,则需要使用以下内容:

$ loginctl session-status \
  | (read session_id ignored; loginctl show-session -p User $session_id)
User=1000

session-statusshow-ssession命令loginctl具有不同的行为而没有参数:session-status使用当前会话,但show-ssession使用管理器。但是,show-session由于脚本可读取,因此使用脚本更可取。这就是为什么loginctl需要两次调用的原因。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.