Comment trouvez-vous l'utilisateur d'origine grâce à plusieurs commandes sudo et su?

93

Lors de l'exécution d'un script via sudo ou su, je veux obtenir l'utilisateur d'origine. Cela devrait se produire indépendamment de plusieurs sudoou sus'exécute à l'intérieur de l'autre et spécifiquement sudo su -.

Evan
la source

Réponses:

136

Résultats:

Utilisez who am i | awk '{print $1}'OR lognamecar aucune autre méthode n'est garantie.

Connecté en tant que soi:

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 normal:

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>

sudo 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 ]#

sudo su -; 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$
Evan
la source
1
Dans ce cas, vous pouvez simplement utiliserwho | awk '{print $1}'
SiegeX
2
... si vous êtes le seul connecté (et ce n'est qu'une seule fois).
Suspendu jusqu'à nouvel ordre.
9
tout ce dont vous avez besoin est de 2 arguments: who am iest le même que who smells bad. De plus, cela ne fonctionne que s'il STDINest associé à un TTY. Donc, si vous l'exécutez, echo "hello" | who am icela ne fonctionnera tout simplement pas.
tylerl
1
Vous ne fonctionneriez pas echo "hello" | who am inormalement, sauf si votre script s'exécute dans un environnement où il n'y a pas de terminal. Ensuite, vous pourriez voir l'erreur qui who am ine fonctionne pas car il y a une sorte de problème avec le stdin non lisible, auquel cas vous pouvez essayer de canaliser les données who am ipar désespoir pour satisfaire ses exigences stdin. tylerl fait juste remarquer qu'il a déjà emprunté cette voie et que le tube ne fonctionnera pas car stdin doit être à la fois lisible et associé à un TTY.
Edwin Buck
4
@even True, même si j'aimerais que cela nécessite le moins de configuration possible, alors j'utilise lognamemaintenant, ce qui en fait fonctionne, où who am ine le fait pas.
Bart van Heukelom
18

Il n'y a pas de parfait réponse . Lorsque vous modifiez les ID utilisateur, l'ID utilisateur d'origine n'est généralement pas conservé et les informations sont donc perdues. Certains programmes, tels que lognameet who -mimplémentent un hack où ils vérifient à quel terminal est connecté stdin, puis vérifient quel utilisateur est connecté sur ce terminal.

Cette solution souvent fonctionne , mais n'est pas infaillible et ne devrait certainement pas être considérée comme sûre. Par exemple, imaginez si whogénère ce qui suit:

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

tomutilisé supour accéder à la racine et exécute votre programme. S'il STDINn'est pas redirigé, un programme comme celui-ci lognamesortiratom . S'il est redirigé (par exemple à partir d'un fichier) comme suit:

logname < /some/file

Alors le résultat est "no login name ", puisque l'entrée n'est pas le terminal. Plus intéressant encore, cependant, est le fait que l'utilisateur peut se faire passer pour un autre utilisateur connecté. Puisque Joe est connecté sur pts / 1, Tom pourrait se faire passer pour lui en exécutant

logname < /dev/pts1

Maintenant, il dit joemême si c'est Tom qui a exécuté la commande. En d'autres termes, si vous utilisez ce mécanisme dans n'importe quel rôle de sécurité, vous êtes fou.

Tylerl
la source
2
Si vous exécutez le script vous-même (comme en témoignent les commandes utilisées), la sécurité n'est pas le problème. Si c'est le cas, vous avez beaucoup plus de problème car ils ont également un accès sudo. La personne peut simplement copier le script et le modifier comme bon lui semble. C'est simplement un moyen d'obtenir le nom connecté à utiliser dans un script. Ou est-ce que je manque quelque chose dans ce que vous dites?
evan le
1
@evan: avoir un accès sudo n'implique pas la possibilité d'écraser des fichiers.
Flimzy
@Flimzy Dans quel cas root n'a-t-il pas la possibilité d'écraser un fichier?
evan
1
@evan: Chaque fois que votre accès sudo ne vous donne pas accès à un shell, ou à toute autre commande qui peut écraser des fichiers, évidemment.
Flimzy
L'accès @evan sudo n'est pas toujours (ce n'est pas le cas dans la plupart des cas d'administration) un accès root total. C'est un ensemble de contextes d'exécution restreints configurables.
DylanYoung
8

C'est une kshfonction que j'ai écrite sur HP-UX. Je ne sais pas comment cela fonctionnera Bashsous Linux. L'idée est que le sudoprocessus s'exécute en tant qu'utilisateur d'origine et que les processus enfants sont l'utilisateur cible. En parcourant les processus parents, nous pouvons trouver l'utilisateur du processus d'origine.

#
# 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
}

Je sais que la question originale remonte à il y a longtemps, mais des gens (comme moi) le demandent toujours et cela semblait être un bon endroit pour mettre la solution.

user1683793
la source
5

Que diriez-vous d'utiliser logname (1) pour obtenir le nom de connexion de l'utilisateur?

sam
la source
logname(1)ne fonctionne pas mais lognamefait - en ajoutant les résultats ci
evan
à l'origine j'avais essayé $LOGNAMEmais cela n'a pas fonctionné. Également ajouté aux résultats ci-dessus.
evan
Nécessite lognametoujours un tty? Avec mes tests, ça passe toujours. (Peut-être que j'ai quelque chose de mal.) J'utilise Linux avec coreutils 8.26.
simohe
Mon nom de journal (GNU coreutils) 8.28 sur renvoie toujours "nom de journal: pas de nom de connexion" (Ubuntu 18.04.2)
sondra.kinsey
5
THIS_USER=`pstree -lu -s $$ | grep --max-count=1 -o '([^)]*)' | head -n 1 | sed 's/[()]//g'`

C'est la seule chose qui a fonctionné pour moi.

Christoforo gris
la source
1
Aucune explication, et seulement une amélioration minime d'une réponse existante
sondra.kinsey
2

La fonction findUser () de user1683793 est portée bashet étendue pour qu'elle renvoie également les noms d'utilisateurs stockés dans les bibliothèques 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"
asdfghjkl
la source
FYI: cette fonction (et celle sur laquelle elle était basée) ne reviendra pas à travers plusieurs shells générés par sudo imbriqués les uns dans les autres.
asdfghjkl
2

revenir en arrière et donner une liste d'utilisateurs

basé sur la réponse de user1683793

En excluant les processus non-TTY, j'ignore root en tant qu'initiateur de la connexion. Je ne suis pas sûr que cela puisse en exclure trop dans certains cas

#!/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
}

lognameou who am ine me donne pas la réponse souhaitée, surtout pas dans les listes plus longues su user1, su user2, su user3,...

Je sais que la question originale remonte à il y a longtemps, mais des gens (comme moi) le demandent toujours et cela semblait être un bon endroit pour mettre la solution.

ULick
la source
2

Alternative à l'appel ps plusieurs fois: faites un appel pstree

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

sortie (une fois connecté comme pair): (evan)

arguments pstree:

  • -l: longues lignes (pas de raccourcissement)
  • -u: afficher lorsque l'utilisateur change en tant que (userName)
  • -s $$: montrer aux parents de ce processus

Obtenez le premier changement d'utilisateur (qui est la connexion) avec grep -oethead .

limitation: la commande peut ne pas contenir d'accolades ()(elle ne le fait normalement pas)

Simohe
la source
pstree -lu -s $$ | head -n1 | sed -e 's / [^ (] * (([^)] *)). * / \ 1 /'
Alexx Roche
0

Sur les systèmes en cours d'exécution systemd-logind, l' API systemd fournit ces informations . Si vous souhaitez accéder à ces informations à partir d'un script shell, vous devez utiliser quelque chose comme ceci:

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

Les commandes système session-statuset show-ssessionde loginctlont un comportement différent sans arguments: session-statusutilise la session en cours, mais show-ssessionutilise le gestionnaire. Cependant, l'utilisation show-sessionest préférable pour l'utilisation de scripts en raison de sa sortie lisible par machine. C'est pourquoi deux invocations de loginctlsont nécessaires.

Florian Weimer
la source